Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
807e74b7d6 | ||
|
|
f4fe2d48c1 | ||
|
|
bc59edff17 | ||
|
|
63d951dc38 | ||
|
|
f1422cb701 | ||
|
|
f7b04f1382 | ||
|
|
7286b5c529 | ||
|
|
6a49fd89a1 | ||
|
|
c4378c2e20 | ||
|
|
6e5ba9ec22 | ||
|
|
502570a18b | ||
|
|
e606c5b24f | ||
|
|
b34a73b08a | ||
|
|
069a42d697 | ||
|
|
2a182a2f5c | ||
|
|
b13f42f8bc | ||
|
|
0d598b854c | ||
|
|
492cee4373 | ||
|
|
a0c53e381c | ||
|
|
46d7e1263c | ||
|
|
426607f916 | ||
|
|
5badcb6143 | ||
|
|
7ebd996eed | ||
|
|
e89b329b54 | ||
|
|
d490cfa8f4 | ||
|
|
abf0d8ca78 | ||
|
|
7f7022b960 | ||
|
|
3a63e14399 | ||
|
|
99ac826a49 | ||
|
|
a3698f93a9 | ||
|
|
94dcc82483 | ||
|
|
9b7499be36 | ||
|
|
018a5feb29 | ||
|
|
4478f99dfe | ||
|
|
ad4c171054 |
@@ -1,23 +0,0 @@
|
||||
App(
|
||||
appid="rolljam",
|
||||
name="RollJam",
|
||||
apptype=FlipperAppType.MENUEXTERNAL,
|
||||
entry_point="rolljam_app",
|
||||
stack_size=4 * 1024,
|
||||
fap_category="Sub-GHz",
|
||||
fap_icon="rolljam.png",
|
||||
fap_icon_assets="images",
|
||||
fap_libs=["assets"],
|
||||
fap_description="RollJam rolling code attack tool",
|
||||
fap_author="@user",
|
||||
fap_version="1.0",
|
||||
fap_weburl="",
|
||||
requires=[
|
||||
"gui",
|
||||
"subghz",
|
||||
"notification",
|
||||
"storage",
|
||||
"dialogs",
|
||||
],
|
||||
provides=[],
|
||||
)
|
||||
@@ -1,521 +0,0 @@
|
||||
#include "rolljam_cc1101_ext.h"
|
||||
#include <furi_hal_gpio.h>
|
||||
#include <furi_hal_resources.h>
|
||||
#include <furi_hal_cortex.h>
|
||||
#include <furi_hal_power.h>
|
||||
|
||||
// ============================================================
|
||||
// 5V OTG power
|
||||
// ============================================================
|
||||
|
||||
static bool otg_was_enabled = false;
|
||||
static bool use_flux_capacitor = false;
|
||||
|
||||
void rolljam_ext_set_flux_capacitor(bool enabled) {
|
||||
use_flux_capacitor = enabled;
|
||||
}
|
||||
|
||||
static void rolljam_ext_power_on(void) {
|
||||
otg_was_enabled = furi_hal_power_is_otg_enabled();
|
||||
if(!otg_was_enabled) {
|
||||
uint8_t attempts = 0;
|
||||
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
|
||||
furi_hal_power_enable_otg();
|
||||
furi_delay_ms(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rolljam_ext_power_off(void) {
|
||||
if(!otg_was_enabled) {
|
||||
furi_hal_power_disable_otg();
|
||||
}
|
||||
}
|
||||
|
||||
static const GpioPin* pin_mosi = &gpio_ext_pa7;
|
||||
static const GpioPin* pin_miso = &gpio_ext_pa6;
|
||||
static const GpioPin* pin_cs = &gpio_ext_pa4;
|
||||
static const GpioPin* pin_sck = &gpio_ext_pb3;
|
||||
static const GpioPin* pin_gdo0 = &gpio_ext_pb2;
|
||||
static const GpioPin* pin_amp = &gpio_ext_pc3;
|
||||
|
||||
// ============================================================
|
||||
// CC1101 Registers
|
||||
// ============================================================
|
||||
#define CC_IOCFG2 0x00
|
||||
#define CC_IOCFG0 0x02
|
||||
#define CC_FIFOTHR 0x03
|
||||
#define CC_SYNC1 0x04
|
||||
#define CC_SYNC0 0x05
|
||||
#define CC_PKTLEN 0x06
|
||||
#define CC_PKTCTRL1 0x07
|
||||
#define CC_PKTCTRL0 0x08
|
||||
#define CC_FSCTRL1 0x0B
|
||||
#define CC_FSCTRL0 0x0C
|
||||
#define CC_FREQ2 0x0D
|
||||
#define CC_FREQ1 0x0E
|
||||
#define CC_FREQ0 0x0F
|
||||
#define CC_MDMCFG4 0x10
|
||||
#define CC_MDMCFG3 0x11
|
||||
#define CC_MDMCFG2 0x12
|
||||
#define CC_MDMCFG1 0x13
|
||||
#define CC_MDMCFG0 0x14
|
||||
#define CC_DEVIATN 0x15
|
||||
#define CC_MCSM1 0x17
|
||||
#define CC_MCSM0 0x18
|
||||
#define CC_FOCCFG 0x19
|
||||
#define CC_AGCCTRL2 0x1B
|
||||
#define CC_AGCCTRL1 0x1C
|
||||
#define CC_AGCCTRL0 0x1D
|
||||
#define CC_FREND0 0x22
|
||||
#define CC_FSCAL3 0x23
|
||||
#define CC_FSCAL2 0x24
|
||||
#define CC_FSCAL1 0x25
|
||||
#define CC_FSCAL0 0x26
|
||||
#define CC_TEST2 0x2C
|
||||
#define CC_TEST1 0x2D
|
||||
#define CC_TEST0 0x2E
|
||||
#define CC_PATABLE 0x3E
|
||||
#define CC_TXFIFO 0x3F
|
||||
|
||||
#define CC_PARTNUM 0x30
|
||||
#define CC_VERSION 0x31
|
||||
#define CC_MARCSTATE 0x35
|
||||
#define CC_TXBYTES 0x3A
|
||||
|
||||
#define CC_SRES 0x30
|
||||
#define CC_SCAL 0x33
|
||||
#define CC_STX 0x35
|
||||
#define CC_SIDLE 0x36
|
||||
#define CC_SFTX 0x3B
|
||||
|
||||
#define MARC_IDLE 0x01
|
||||
#define MARC_TX 0x13
|
||||
|
||||
// ============================================================
|
||||
// Band calibration
|
||||
// ============================================================
|
||||
|
||||
typedef struct {
|
||||
uint32_t min_freq;
|
||||
uint32_t max_freq;
|
||||
uint8_t fscal3;
|
||||
uint8_t fscal2;
|
||||
uint8_t fscal1;
|
||||
uint8_t fscal0;
|
||||
} ExtBandCal;
|
||||
|
||||
static const ExtBandCal ext_band_cals[] = {
|
||||
{ 299000000, 348000000, 0xEA, 0x2A, 0x00, 0x1F },
|
||||
{ 386000000, 464000000, 0xE9, 0x2A, 0x00, 0x1F },
|
||||
{ 778000000, 928000000, 0xEA, 0x2A, 0x00, 0x11 },
|
||||
};
|
||||
#define EXT_BAND_CAL_COUNT (sizeof(ext_band_cals) / sizeof(ext_band_cals[0]))
|
||||
|
||||
static const ExtBandCal* ext_get_band_cal(uint32_t freq) {
|
||||
for(size_t i = 0; i < EXT_BAND_CAL_COUNT; i++) {
|
||||
if(freq >= ext_band_cals[i].min_freq && freq <= ext_band_cals[i].max_freq)
|
||||
return &ext_band_cals[i];
|
||||
}
|
||||
return &ext_band_cals[1];
|
||||
}
|
||||
|
||||
static inline void spi_delay(void) {
|
||||
for(int i = 0; i < 16; i++) __NOP();
|
||||
}
|
||||
|
||||
static inline void cs_lo(void) { furi_hal_gpio_write(pin_cs, false); spi_delay(); }
|
||||
static inline void cs_hi(void) { spi_delay(); furi_hal_gpio_write(pin_cs, true); spi_delay(); }
|
||||
|
||||
static bool wait_miso(uint32_t us) {
|
||||
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
|
||||
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
|
||||
uint32_t s = DWT->CYCCNT;
|
||||
uint32_t t = (SystemCoreClock / 1000000) * us;
|
||||
while(furi_hal_gpio_read(pin_miso)) {
|
||||
if((DWT->CYCCNT - s) > t) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint8_t spi_byte(uint8_t tx) {
|
||||
uint8_t rx = 0;
|
||||
for(int8_t i = 7; i >= 0; i--) {
|
||||
furi_hal_gpio_write(pin_mosi, (tx >> i) & 0x01);
|
||||
spi_delay();
|
||||
furi_hal_gpio_write(pin_sck, true);
|
||||
spi_delay();
|
||||
if(furi_hal_gpio_read(pin_miso)) rx |= (1 << i);
|
||||
furi_hal_gpio_write(pin_sck, false);
|
||||
spi_delay();
|
||||
}
|
||||
return rx;
|
||||
}
|
||||
|
||||
static uint8_t cc_strobe(uint8_t cmd) {
|
||||
cs_lo();
|
||||
if(!wait_miso(5000)) { cs_hi(); return 0xFF; }
|
||||
uint8_t s = spi_byte(cmd);
|
||||
cs_hi();
|
||||
return s;
|
||||
}
|
||||
|
||||
static void cc_write(uint8_t a, uint8_t v) {
|
||||
cs_lo();
|
||||
if(!wait_miso(5000)) { cs_hi(); return; }
|
||||
spi_byte(a); spi_byte(v);
|
||||
cs_hi();
|
||||
}
|
||||
|
||||
static uint8_t cc_read_status(uint8_t a) {
|
||||
cs_lo();
|
||||
if(!wait_miso(5000)) { cs_hi(); return 0xFF; }
|
||||
spi_byte(a | 0xC0);
|
||||
uint8_t v = spi_byte(0x00);
|
||||
cs_hi();
|
||||
return v;
|
||||
}
|
||||
|
||||
static void cc_write_burst(uint8_t a, const uint8_t* d, uint8_t n) {
|
||||
cs_lo();
|
||||
if(!wait_miso(5000)) { cs_hi(); return; }
|
||||
spi_byte(a | 0x40);
|
||||
for(uint8_t i = 0; i < n; i++) spi_byte(d[i]);
|
||||
cs_hi();
|
||||
}
|
||||
|
||||
static bool cc_reset(void) {
|
||||
cs_hi(); furi_delay_us(30);
|
||||
cs_lo(); furi_delay_us(30);
|
||||
cs_hi(); furi_delay_us(50);
|
||||
cs_lo();
|
||||
if(!wait_miso(10000)) { cs_hi(); return false; }
|
||||
spi_byte(CC_SRES);
|
||||
if(!wait_miso(100000)) { cs_hi(); return false; }
|
||||
cs_hi();
|
||||
furi_delay_ms(5);
|
||||
FURI_LOG_I(TAG, "EXT: Reset OK");
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cc_check(void) {
|
||||
uint8_t p = cc_read_status(CC_PARTNUM);
|
||||
uint8_t v = cc_read_status(CC_VERSION);
|
||||
FURI_LOG_I(TAG, "EXT: PART=0x%02X VER=0x%02X", p, v);
|
||||
return (v == 0x14 || v == 0x04 || v == 0x03);
|
||||
}
|
||||
|
||||
static uint8_t cc_state(void) { return cc_read_status(CC_MARCSTATE) & 0x1F; }
|
||||
static uint8_t cc_txbytes(void) { return cc_read_status(CC_TXBYTES) & 0x7F; }
|
||||
|
||||
static void cc_idle(void) {
|
||||
cc_strobe(CC_SIDLE);
|
||||
for(int i = 0; i < 500; i++) {
|
||||
if(cc_state() == MARC_IDLE) return;
|
||||
furi_delay_us(50);
|
||||
}
|
||||
}
|
||||
|
||||
static void cc_set_freq(uint32_t f) {
|
||||
uint32_t r = (uint32_t)(((uint64_t)f << 16) / 26000000ULL);
|
||||
cc_write(CC_FREQ2, (r >> 16) & 0xFF);
|
||||
cc_write(CC_FREQ1, (r >> 8) & 0xFF);
|
||||
cc_write(CC_FREQ0, r & 0xFF);
|
||||
}
|
||||
|
||||
static bool cc_configure_jam(uint32_t freq) {
|
||||
const ExtBandCal* cal = ext_get_band_cal(freq);
|
||||
FURI_LOG_I(TAG, "EXT: Config OOK jam at %lu Hz", freq);
|
||||
cc_idle();
|
||||
cc_write(CC_IOCFG0, 0x02);
|
||||
cc_write(CC_IOCFG2, 0x2F);
|
||||
cc_write(CC_PKTCTRL0, 0x00);
|
||||
cc_write(CC_PKTCTRL1, 0x00);
|
||||
cc_write(CC_PKTLEN, 0xFF);
|
||||
cc_write(CC_FIFOTHR, 0x07);
|
||||
cc_write(CC_SYNC1, 0x00);
|
||||
cc_write(CC_SYNC0, 0x00);
|
||||
cc_set_freq(freq);
|
||||
cc_write(CC_FSCTRL1, 0x06);
|
||||
cc_write(CC_FSCTRL0, 0x00);
|
||||
cc_write(CC_MDMCFG4, 0x85);
|
||||
cc_write(CC_MDMCFG3, 0x43);
|
||||
cc_write(CC_MDMCFG2, 0x30);
|
||||
cc_write(CC_MDMCFG1, 0x00);
|
||||
cc_write(CC_MDMCFG0, 0xF8);
|
||||
cc_write(CC_DEVIATN, 0x47);
|
||||
cc_write(CC_MCSM1, 0x00);
|
||||
cc_write(CC_MCSM0, 0x18);
|
||||
cc_write(CC_FREND0, 0x11);
|
||||
uint8_t pa[8] = {0x00,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0};
|
||||
cc_write_burst(CC_PATABLE, pa, 8);
|
||||
cc_write(CC_FSCAL3, cal->fscal3);
|
||||
cc_write(CC_FSCAL2, cal->fscal2);
|
||||
cc_write(CC_FSCAL1, cal->fscal1);
|
||||
cc_write(CC_FSCAL0, cal->fscal0);
|
||||
cc_write(CC_TEST2, 0x81);
|
||||
cc_write(CC_TEST1, 0x35);
|
||||
cc_write(CC_TEST0, 0x09);
|
||||
cc_idle();
|
||||
cc_strobe(CC_SCAL);
|
||||
furi_delay_ms(2);
|
||||
cc_idle();
|
||||
uint8_t st = cc_state();
|
||||
FURI_LOG_I(TAG, "EXT: state=0x%02X FSCAL={0x%02X,0x%02X,0x%02X,0x%02X}",
|
||||
st, cal->fscal3, cal->fscal2, cal->fscal1, cal->fscal0);
|
||||
return (st == MARC_IDLE);
|
||||
}
|
||||
|
||||
static bool cc_configure_jam_fsk(uint32_t freq, bool wide) {
|
||||
const ExtBandCal* cal = ext_get_band_cal(freq);
|
||||
FURI_LOG_I(TAG, "EXT: Config FSK jam at %lu Hz (wide=%d)", freq, wide);
|
||||
cc_idle();
|
||||
cc_write(CC_IOCFG0, 0x02);
|
||||
cc_write(CC_IOCFG2, 0x2F);
|
||||
cc_write(CC_PKTCTRL0, 0x00);
|
||||
cc_write(CC_PKTCTRL1, 0x00);
|
||||
cc_write(CC_PKTLEN, 0xFF);
|
||||
cc_write(CC_FIFOTHR, 0x07);
|
||||
cc_write(CC_SYNC1, 0x00);
|
||||
cc_write(CC_SYNC0, 0x00);
|
||||
cc_set_freq(freq);
|
||||
cc_write(CC_FSCTRL1, 0x06);
|
||||
cc_write(CC_FSCTRL0, 0x00);
|
||||
cc_write(CC_MDMCFG4, 0x85);
|
||||
cc_write(CC_MDMCFG3, 0x43);
|
||||
cc_write(CC_MDMCFG2, 0x00);
|
||||
cc_write(CC_MDMCFG1, 0x00);
|
||||
cc_write(CC_MDMCFG0, 0xF8);
|
||||
cc_write(CC_DEVIATN, wide ? 0x47 : 0x15);
|
||||
cc_write(CC_MCSM1, 0x00);
|
||||
cc_write(CC_MCSM0, 0x18);
|
||||
cc_write(CC_FREND0, 0x10);
|
||||
uint8_t pa[8] = {0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0};
|
||||
cc_write_burst(CC_PATABLE, pa, 8);
|
||||
cc_write(CC_FSCAL3, cal->fscal3);
|
||||
cc_write(CC_FSCAL2, cal->fscal2);
|
||||
cc_write(CC_FSCAL1, cal->fscal1);
|
||||
cc_write(CC_FSCAL0, cal->fscal0);
|
||||
cc_write(CC_TEST2, 0x81);
|
||||
cc_write(CC_TEST1, 0x35);
|
||||
cc_write(CC_TEST0, 0x09);
|
||||
cc_idle();
|
||||
cc_strobe(CC_SCAL);
|
||||
furi_delay_ms(2);
|
||||
cc_idle();
|
||||
return (cc_state() == MARC_IDLE);
|
||||
}
|
||||
|
||||
static void ext_gpio_init_spi_pins(void) {
|
||||
furi_hal_gpio_init(pin_cs, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
|
||||
furi_hal_gpio_write(pin_cs, true);
|
||||
furi_hal_gpio_init(pin_sck, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
|
||||
furi_hal_gpio_write(pin_sck, false);
|
||||
furi_hal_gpio_init(pin_mosi, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
|
||||
furi_hal_gpio_write(pin_mosi, false);
|
||||
furi_hal_gpio_init(pin_miso, GpioModeInput, GpioPullUp, GpioSpeedVeryHigh);
|
||||
furi_hal_gpio_init(pin_gdo0, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh);
|
||||
}
|
||||
|
||||
static void ext_gpio_deinit_spi_pins(void) {
|
||||
furi_hal_gpio_init(pin_cs, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(pin_sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(pin_mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(pin_miso, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(pin_gdo0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
}
|
||||
|
||||
void rolljam_ext_gpio_init(void) {
|
||||
FURI_LOG_I(TAG, "EXT GPIO init (deferred to jam thread)");
|
||||
if(use_flux_capacitor) {
|
||||
furi_hal_gpio_init_simple(pin_amp, GpioModeOutputPushPull);
|
||||
furi_hal_gpio_write(pin_amp, false);
|
||||
}
|
||||
}
|
||||
|
||||
void rolljam_ext_gpio_deinit(void) {
|
||||
if(use_flux_capacitor) {
|
||||
furi_hal_gpio_write(pin_amp, false);
|
||||
furi_hal_gpio_init_simple(pin_amp, GpioModeAnalog);
|
||||
}
|
||||
|
||||
FURI_LOG_I(TAG, "EXT GPIO deinit");
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Noise pattern & jam helpers
|
||||
// ============================================================
|
||||
|
||||
static void jam_start_tx(const uint8_t* pattern, uint8_t len) {
|
||||
cc_strobe(CC_SFTX);
|
||||
furi_delay_ms(1);
|
||||
cc_write_burst(CC_TXFIFO, pattern, len);
|
||||
cc_strobe(CC_STX);
|
||||
furi_delay_ms(5);
|
||||
}
|
||||
|
||||
static int32_t jam_thread_worker(void* context) {
|
||||
RollJamApp* app = context;
|
||||
|
||||
bool is_fsk = (app->mod_index == ModIndex_FM238 || app->mod_index == ModIndex_FM476);
|
||||
uint32_t freq_pos = app->frequency + app->jam_offset_hz;
|
||||
uint32_t freq_neg = app->frequency - app->jam_offset_hz;
|
||||
|
||||
FURI_LOG_I(TAG, "JAM thread start: target=%lu offset=%lu FSK=%d",
|
||||
app->frequency, app->jam_offset_hz, is_fsk);
|
||||
|
||||
ext_gpio_init_spi_pins();
|
||||
furi_delay_ms(5);
|
||||
|
||||
if(!cc_reset()) {
|
||||
FURI_LOG_E(TAG, "JAM: Reset failed — CC1101 externo no conectado o mal cableado");
|
||||
ext_gpio_deinit_spi_pins();
|
||||
app->jamming_active = false;
|
||||
return -1;
|
||||
}
|
||||
if(!cc_check()) {
|
||||
FURI_LOG_E(TAG, "JAM: Chip no detectado");
|
||||
ext_gpio_deinit_spi_pins();
|
||||
app->jamming_active = false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool jam_ok;
|
||||
if(app->mod_index == ModIndex_FM238)
|
||||
jam_ok = cc_configure_jam_fsk(freq_pos, false);
|
||||
else if(app->mod_index == ModIndex_FM476)
|
||||
jam_ok = cc_configure_jam_fsk(freq_pos, true);
|
||||
else
|
||||
jam_ok = cc_configure_jam(freq_pos);
|
||||
|
||||
if(!jam_ok) {
|
||||
FURI_LOG_E(TAG, "JAM: Config failed");
|
||||
ext_gpio_deinit_spi_pins();
|
||||
app->jamming_active = false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const uint8_t noise_pattern[62] = {
|
||||
0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,
|
||||
0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,
|
||||
0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,
|
||||
0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,
|
||||
0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,
|
||||
0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,
|
||||
0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,
|
||||
0xAA,0x55
|
||||
};
|
||||
|
||||
if(use_flux_capacitor) furi_hal_gpio_write(pin_amp, true);
|
||||
jam_start_tx(noise_pattern, 62);
|
||||
|
||||
uint8_t st = cc_state();
|
||||
if(st != MARC_TX) {
|
||||
cc_idle();
|
||||
jam_start_tx(noise_pattern, 62);
|
||||
st = cc_state();
|
||||
if(st != MARC_TX) {
|
||||
FURI_LOG_E(TAG, "JAM: Cannot enter TX (state=0x%02X)", st);
|
||||
if(use_flux_capacitor) furi_hal_gpio_write(pin_amp, false);
|
||||
ext_gpio_deinit_spi_pins();
|
||||
app->jamming_active = false;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
FURI_LOG_I(TAG, "JAM: *** ACTIVE *** freq_pos=%lu", freq_pos);
|
||||
|
||||
uint32_t loops = 0;
|
||||
uint32_t underflows = 0;
|
||||
uint32_t refills = 0;
|
||||
bool on_pos = true;
|
||||
|
||||
while(app->jam_thread_running) {
|
||||
loops++;
|
||||
|
||||
if(is_fsk && (loops % 4 == 0)) {
|
||||
cc_idle();
|
||||
cc_strobe(CC_SFTX);
|
||||
furi_delay_us(100);
|
||||
on_pos = !on_pos;
|
||||
cc_set_freq(on_pos ? freq_pos : freq_neg);
|
||||
cc_write_burst(CC_TXFIFO, noise_pattern, 62);
|
||||
cc_strobe(CC_STX);
|
||||
furi_delay_ms(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
st = cc_state();
|
||||
if(st != MARC_TX) {
|
||||
underflows++;
|
||||
cc_idle();
|
||||
cc_strobe(CC_SFTX);
|
||||
furi_delay_us(100);
|
||||
cc_write_burst(CC_TXFIFO, noise_pattern, 62);
|
||||
cc_strobe(CC_STX);
|
||||
furi_delay_ms(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t txb = cc_txbytes();
|
||||
if(txb < 20) {
|
||||
uint8_t space = 62 - txb;
|
||||
if(space > 50) space = 50;
|
||||
cc_write_burst(CC_TXFIFO, noise_pattern, space);
|
||||
refills++;
|
||||
}
|
||||
|
||||
if(loops % 500 == 0) {
|
||||
FURI_LOG_I(TAG, "JAM: loops=%lu uf=%lu refills=%lu txb=%d",
|
||||
loops, underflows, refills, cc_txbytes());
|
||||
}
|
||||
|
||||
furi_delay_ms(50);
|
||||
}
|
||||
|
||||
cc_idle();
|
||||
if(use_flux_capacitor) furi_hal_gpio_write(pin_amp, false);
|
||||
cc_write(CC_IOCFG2, 0x2E);
|
||||
|
||||
ext_gpio_deinit_spi_pins();
|
||||
|
||||
FURI_LOG_I(TAG, "JAM: STOPPED (loops=%lu uf=%lu refills=%lu)", loops, underflows, refills);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Public API
|
||||
// ============================================================
|
||||
|
||||
void rolljam_jammer_start(RollJamApp* app) {
|
||||
if(app->jamming_active) return;
|
||||
|
||||
app->jam_frequency = app->frequency + app->jam_offset_hz;
|
||||
app->jam_thread_running = true;
|
||||
app->jamming_active = true;
|
||||
|
||||
rolljam_ext_power_on();
|
||||
furi_delay_ms(50);
|
||||
|
||||
rolljam_ext_gpio_init();
|
||||
|
||||
app->jam_thread = furi_thread_alloc_ex("RJ_Jam", 4096, jam_thread_worker, app);
|
||||
furi_thread_start(app->jam_thread);
|
||||
|
||||
FURI_LOG_I(TAG, ">>> JAMMER THREAD STARTED <<<");
|
||||
}
|
||||
|
||||
void rolljam_jammer_stop(RollJamApp* app) {
|
||||
if(!app->jamming_active) return;
|
||||
|
||||
app->jam_thread_running = false;
|
||||
furi_thread_join(app->jam_thread);
|
||||
furi_thread_free(app->jam_thread);
|
||||
app->jam_thread = NULL;
|
||||
|
||||
rolljam_ext_gpio_deinit();
|
||||
rolljam_ext_power_off();
|
||||
app->jamming_active = false;
|
||||
|
||||
FURI_LOG_I(TAG, ">>> JAMMER STOPPED <<<");
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../rolljam.h"
|
||||
|
||||
/*
|
||||
* External CC1101 module connected via GPIO (bit-bang SPI).
|
||||
* Used EXCLUSIVELY for JAMMING (TX).
|
||||
*
|
||||
* Wiring (as connected):
|
||||
* CC1101 VCC -> Flipper Pin 9 (3V3)
|
||||
* CC1101 GND -> Flipper Pin 11 (GND)
|
||||
* CC1101 MOSI -> Flipper Pin 2 (PA7)
|
||||
* CC1101 MISO -> Flipper Pin 3 (PA6)
|
||||
* CC1101 SCK -> Flipper Pin 5 (PB3)
|
||||
* CC1101 CS -> Flipper Pin 4 (PA4)
|
||||
* CC1101 GDO0 -> Flipper Pin 6 (PB2)
|
||||
*/
|
||||
|
||||
void rolljam_ext_gpio_init(void);
|
||||
void rolljam_ext_set_flux_capacitor(bool enabled);
|
||||
void rolljam_ext_gpio_deinit(void);
|
||||
void rolljam_jammer_start(RollJamApp* app);
|
||||
void rolljam_jammer_stop(RollJamApp* app);
|
||||
@@ -1,689 +0,0 @@
|
||||
#include "rolljam_receiver.h"
|
||||
#include <furi_hal_subghz.h>
|
||||
#include <furi_hal_rtc.h>
|
||||
|
||||
#define CC_IOCFG0 0x02
|
||||
#define CC_FIFOTHR 0x03
|
||||
#define CC_MDMCFG4 0x10
|
||||
#define CC_MDMCFG3 0x11
|
||||
#define CC_MDMCFG2 0x12
|
||||
#define CC_MDMCFG1 0x13
|
||||
#define CC_MDMCFG0 0x14
|
||||
#define CC_DEVIATN 0x15
|
||||
#define CC_MCSM0 0x18
|
||||
#define CC_FOCCFG 0x19
|
||||
#define CC_AGCCTRL2 0x1B
|
||||
#define CC_AGCCTRL1 0x1C
|
||||
#define CC_AGCCTRL0 0x1D
|
||||
#define CC_FREND0 0x22
|
||||
#define CC_FSCAL3 0x23
|
||||
#define CC_FSCAL2 0x24
|
||||
#define CC_FSCAL1 0x25
|
||||
#define CC_FSCAL0 0x26
|
||||
|
||||
#define CC_PKTCTRL0 0x08
|
||||
#define CC_PKTCTRL1 0x07
|
||||
#define CC_FSCTRL1 0x0B
|
||||
#define CC_WORCTRL 0x20
|
||||
#define CC_FREND1 0x21
|
||||
|
||||
// OOK 650kHz
|
||||
static const uint8_t preset_ook_650_async[] = {
|
||||
CC_IOCFG0, 0x0D,
|
||||
CC_FIFOTHR, 0x07,
|
||||
CC_PKTCTRL0, 0x32,
|
||||
CC_FSCTRL1, 0x06,
|
||||
CC_MDMCFG0, 0x00,
|
||||
CC_MDMCFG1, 0x00,
|
||||
CC_MDMCFG2, 0x30,
|
||||
CC_MDMCFG3, 0x32,
|
||||
CC_MDMCFG4, 0x17,
|
||||
CC_MCSM0, 0x18,
|
||||
CC_FOCCFG, 0x18,
|
||||
CC_AGCCTRL0, 0x91,
|
||||
CC_AGCCTRL1, 0x00,
|
||||
CC_AGCCTRL2, 0x07,
|
||||
CC_WORCTRL, 0xFB,
|
||||
CC_FREND0, 0x11,
|
||||
CC_FREND1, 0xB6,
|
||||
0x00, 0x00,
|
||||
0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
// OOK 270kHz
|
||||
static const uint8_t preset_ook_270_async[] = {
|
||||
CC_IOCFG0, 0x0D,
|
||||
CC_FIFOTHR, 0x47,
|
||||
CC_PKTCTRL0, 0x32,
|
||||
CC_FSCTRL1, 0x06,
|
||||
CC_MDMCFG0, 0x00,
|
||||
CC_MDMCFG1, 0x00,
|
||||
CC_MDMCFG2, 0x30,
|
||||
CC_MDMCFG3, 0x32,
|
||||
CC_MDMCFG4, 0x67,
|
||||
CC_MCSM0, 0x18,
|
||||
CC_FOCCFG, 0x18,
|
||||
CC_AGCCTRL0, 0x40,
|
||||
CC_AGCCTRL1, 0x00,
|
||||
CC_AGCCTRL2, 0x03,
|
||||
CC_WORCTRL, 0xFB,
|
||||
CC_FREND0, 0x11,
|
||||
CC_FREND1, 0xB6,
|
||||
0x00, 0x00,
|
||||
0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
// 2FSK Dev 2.38kHz
|
||||
static const uint8_t preset_2fsk_238_async[] = {
|
||||
CC_IOCFG0, 0x0D,
|
||||
CC_FIFOTHR, 0x47,
|
||||
CC_PKTCTRL0, 0x32,
|
||||
CC_FSCTRL1, 0x06,
|
||||
CC_MDMCFG0, 0x00,
|
||||
CC_MDMCFG1, 0x00,
|
||||
CC_MDMCFG2, 0x00,
|
||||
CC_MDMCFG3, 0x75,
|
||||
CC_MDMCFG4, 0x57,
|
||||
CC_DEVIATN, 0x15,
|
||||
CC_MCSM0, 0x18,
|
||||
CC_FOCCFG, 0x16,
|
||||
CC_AGCCTRL0, 0x91,
|
||||
CC_AGCCTRL1, 0x00,
|
||||
CC_AGCCTRL2, 0x07,
|
||||
CC_WORCTRL, 0xFB,
|
||||
CC_FREND0, 0x10,
|
||||
CC_FREND1, 0xB6,
|
||||
0x00, 0x00,
|
||||
0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
// 2FSK Dev 47.6kHz
|
||||
static const uint8_t preset_2fsk_476_async[] = {
|
||||
CC_IOCFG0, 0x0D,
|
||||
CC_FIFOTHR, 0x47,
|
||||
CC_PKTCTRL0, 0x32,
|
||||
CC_FSCTRL1, 0x06,
|
||||
CC_MDMCFG0, 0x00,
|
||||
CC_MDMCFG1, 0x00,
|
||||
CC_MDMCFG2, 0x00,
|
||||
CC_MDMCFG3, 0x75,
|
||||
CC_MDMCFG4, 0x57,
|
||||
CC_DEVIATN, 0x47,
|
||||
CC_MCSM0, 0x18,
|
||||
CC_FOCCFG, 0x16,
|
||||
CC_AGCCTRL0, 0x91,
|
||||
CC_AGCCTRL1, 0x00,
|
||||
CC_AGCCTRL2, 0x07,
|
||||
CC_WORCTRL, 0xFB,
|
||||
CC_FREND0, 0x10,
|
||||
CC_FREND1, 0xB6,
|
||||
0x00, 0x00,
|
||||
0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
// TX OOK
|
||||
static const uint8_t preset_ook_tx[] = {
|
||||
CC_IOCFG0, 0x0D,
|
||||
CC_FIFOTHR, 0x07,
|
||||
CC_PKTCTRL0, 0x32,
|
||||
CC_FSCTRL1, 0x06,
|
||||
CC_MDMCFG0, 0x00,
|
||||
CC_MDMCFG1, 0x00,
|
||||
CC_MDMCFG2, 0x30,
|
||||
CC_MDMCFG3, 0x32,
|
||||
CC_MDMCFG4, 0x17,
|
||||
CC_MCSM0, 0x18,
|
||||
CC_FOCCFG, 0x18,
|
||||
CC_AGCCTRL0, 0x91,
|
||||
CC_AGCCTRL1, 0x00,
|
||||
CC_AGCCTRL2, 0x07,
|
||||
CC_WORCTRL, 0xFB,
|
||||
CC_FREND0, 0x11,
|
||||
CC_FREND1, 0xB6,
|
||||
0x00, 0x00,
|
||||
0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const uint8_t preset_fsk_tx_238[] = {
|
||||
CC_IOCFG0, 0x0D,
|
||||
CC_FIFOTHR, 0x47,
|
||||
CC_PKTCTRL0, 0x32,
|
||||
CC_FSCTRL1, 0x06,
|
||||
CC_MDMCFG0, 0x00,
|
||||
CC_MDMCFG1, 0x00,
|
||||
CC_MDMCFG2, 0x00,
|
||||
CC_MDMCFG3, 0x75,
|
||||
CC_MDMCFG4, 0x57,
|
||||
CC_DEVIATN, 0x15,
|
||||
CC_MCSM0, 0x18,
|
||||
CC_FOCCFG, 0x16,
|
||||
CC_AGCCTRL0, 0x91,
|
||||
CC_AGCCTRL1, 0x00,
|
||||
CC_AGCCTRL2, 0x07,
|
||||
CC_WORCTRL, 0xFB,
|
||||
CC_FREND0, 0x10,
|
||||
CC_FREND1, 0xB6,
|
||||
0x00, 0x00,
|
||||
0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const uint8_t preset_fsk_tx_476[] = {
|
||||
CC_IOCFG0, 0x0D,
|
||||
CC_FIFOTHR, 0x47,
|
||||
CC_PKTCTRL0, 0x32,
|
||||
CC_FSCTRL1, 0x06,
|
||||
CC_MDMCFG0, 0x00,
|
||||
CC_MDMCFG1, 0x00,
|
||||
CC_MDMCFG2, 0x00,
|
||||
CC_MDMCFG3, 0x75,
|
||||
CC_MDMCFG4, 0x57,
|
||||
CC_DEVIATN, 0x47,
|
||||
CC_MCSM0, 0x18,
|
||||
CC_FOCCFG, 0x16,
|
||||
CC_AGCCTRL0, 0x91,
|
||||
CC_AGCCTRL1, 0x00,
|
||||
CC_AGCCTRL2, 0x07,
|
||||
CC_WORCTRL, 0xFB,
|
||||
CC_FREND0, 0x10,
|
||||
CC_FREND1, 0xB6,
|
||||
0x00, 0x00,
|
||||
0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
// Capture state machine
|
||||
// ============================================================
|
||||
|
||||
#define MIN_PULSE_US 100
|
||||
#define MAX_PULSE_US 32767
|
||||
#define SILENCE_GAP_US 50000
|
||||
#define MIN_FRAME_PULSES 40
|
||||
#define AUTO_ACCEPT_PULSES 300
|
||||
#define MAX_CONTINUOUS_SAMPLES 800
|
||||
|
||||
static bool rolljam_is_jammer_pattern_mod(RawSignal* s, uint8_t mod_index) {
|
||||
if(s->size < 20) return false;
|
||||
|
||||
// Calcular estadísticas una sola vez
|
||||
int16_t max_abs = 0;
|
||||
int64_t sum = 0;
|
||||
for(size_t i = 0; i < s->size; i++) {
|
||||
int16_t v = s->data[i] > 0 ? s->data[i] : -s->data[i];
|
||||
if(v > max_abs) max_abs = v;
|
||||
sum += v;
|
||||
}
|
||||
int32_t mean = (int32_t)(sum / (int64_t)s->size);
|
||||
|
||||
FURI_LOG_D(TAG, "JamCheck: mod=%d max=%d mean=%ld size=%d",
|
||||
mod_index, max_abs, mean, (int)s->size);
|
||||
|
||||
if(mod_index == 2 || mod_index == 3) { // ModIndex_FM238=2, FM476=3
|
||||
if((int)s->size < 120) {
|
||||
FURI_LOG_W(TAG, "Jammer FSK rechazado: size=%d < 120", (int)s->size);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if(max_abs < 25000) {
|
||||
FURI_LOG_W(TAG, "Jammer AM650 rechazado: max=%d < 25000", max_abs);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(mod_index == 1) { // ModIndex_AM270=1
|
||||
if(mean < 3000) {
|
||||
FURI_LOG_W(TAG, "Jammer AM270 rechazado: mean=%ld < 3000 (max=%d)", mean, max_abs);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define MIN_VARIANCE 2000
|
||||
|
||||
static bool rolljam_has_sufficient_variance(RawSignal* s) {
|
||||
if(s->size < 20) return false;
|
||||
|
||||
int64_t sum = 0;
|
||||
for(size_t i = 0; i < s->size; i++) {
|
||||
int16_t val = s->data[i];
|
||||
sum += (val > 0) ? val : -val;
|
||||
}
|
||||
int32_t mean = (int32_t)(sum / (int64_t)s->size);
|
||||
|
||||
int64_t var_sum = 0;
|
||||
for(size_t i = 0; i < s->size; i++) {
|
||||
int16_t val = s->data[i];
|
||||
int32_t abs_val = (val > 0) ? val : -val;
|
||||
int32_t diff = abs_val - mean;
|
||||
var_sum += (int64_t)diff * diff;
|
||||
}
|
||||
int32_t variance = (int32_t)(var_sum / (int64_t)s->size);
|
||||
|
||||
bool has_var = (variance > MIN_VARIANCE);
|
||||
FURI_LOG_I(TAG, "Variance: mean=%ld var=%ld %s",
|
||||
mean, variance, has_var ? "PASS" : "FAIL");
|
||||
return has_var;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
CapWaiting,
|
||||
CapRecording,
|
||||
CapDone,
|
||||
} CapState;
|
||||
|
||||
typedef struct {
|
||||
volatile CapState state;
|
||||
volatile int valid_count;
|
||||
volatile int total_count;
|
||||
volatile bool target_first;
|
||||
volatile uint32_t callback_count;
|
||||
volatile uint32_t continuous_count;
|
||||
float rssi_baseline;
|
||||
uint8_t mod_index;
|
||||
} CapCtx;
|
||||
|
||||
static CapCtx g_cap;
|
||||
|
||||
static void cap_ctx_reset(CapCtx* c) {
|
||||
c->state = CapWaiting;
|
||||
c->valid_count = 0;
|
||||
c->total_count = 0;
|
||||
c->callback_count = 0;
|
||||
c->continuous_count = 0;
|
||||
}
|
||||
|
||||
static void capture_rx_callback(bool level, uint32_t duration, void* context) {
|
||||
RollJamApp* app = context;
|
||||
|
||||
if(!app->raw_capture_active) return;
|
||||
if(g_cap.state == CapDone) return;
|
||||
|
||||
g_cap.callback_count++;
|
||||
|
||||
RawSignal* target = g_cap.target_first ? &app->signal_first : &app->signal_second;
|
||||
if(target->valid) return;
|
||||
|
||||
uint32_t dur = duration;
|
||||
bool is_silence = (dur > SILENCE_GAP_US);
|
||||
bool is_medium_gap = (dur > 5000 && dur <= SILENCE_GAP_US);
|
||||
if(dur > 32767) dur = 32767;
|
||||
|
||||
switch(g_cap.state) {
|
||||
case CapWaiting:
|
||||
g_cap.continuous_count = 0;
|
||||
if(dur >= MIN_PULSE_US && dur <= MAX_PULSE_US && !is_silence) {
|
||||
target->size = 0;
|
||||
g_cap.valid_count = 0;
|
||||
g_cap.total_count = 0;
|
||||
g_cap.state = CapRecording;
|
||||
int16_t s = level ? (int16_t)dur : -(int16_t)dur;
|
||||
target->data[target->size++] = s;
|
||||
g_cap.valid_count++;
|
||||
g_cap.total_count++;
|
||||
g_cap.continuous_count = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case CapRecording:
|
||||
g_cap.continuous_count++;
|
||||
|
||||
if(g_cap.continuous_count > MAX_CONTINUOUS_SAMPLES && !is_medium_gap && !is_silence) {
|
||||
target->size = 0;
|
||||
cap_ctx_reset(&g_cap);
|
||||
return;
|
||||
}
|
||||
|
||||
if(target->size >= RAW_SIGNAL_MAX_SIZE) {
|
||||
g_cap.state = (g_cap.valid_count >= MIN_FRAME_PULSES) ? CapDone : CapWaiting;
|
||||
if(g_cap.state == CapWaiting) {
|
||||
target->size = 0;
|
||||
g_cap.valid_count = 0;
|
||||
g_cap.total_count = 0;
|
||||
g_cap.continuous_count = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(is_silence) {
|
||||
if(g_cap.valid_count >= MIN_FRAME_PULSES) {
|
||||
if(target->size < RAW_SIGNAL_MAX_SIZE)
|
||||
target->data[target->size++] = level ? (int16_t)32767 : -32767;
|
||||
g_cap.state = CapDone;
|
||||
} else {
|
||||
target->size = 0;
|
||||
cap_ctx_reset(&g_cap);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(is_medium_gap) g_cap.continuous_count = 0;
|
||||
|
||||
{
|
||||
int16_t s = level ? (int16_t)dur : -(int16_t)dur;
|
||||
target->data[target->size++] = s;
|
||||
g_cap.total_count++;
|
||||
if(dur >= MIN_PULSE_US && dur <= MAX_PULSE_US) {
|
||||
g_cap.valid_count++;
|
||||
if(g_cap.valid_count >= AUTO_ACCEPT_PULSES)
|
||||
g_cap.state = CapDone;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CapDone:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Capture start/stop
|
||||
// ============================================================
|
||||
|
||||
void rolljam_capture_start(RollJamApp* app) {
|
||||
FURI_LOG_I(TAG, "Capture start: freq=%lu mod=%d offset=%lu",
|
||||
app->frequency, app->mod_index, app->jam_offset_hz);
|
||||
|
||||
const uint8_t* src_preset;
|
||||
switch(app->mod_index) {
|
||||
case ModIndex_AM270: src_preset = preset_ook_270_async; break;
|
||||
case ModIndex_FM238: src_preset = preset_2fsk_238_async; break;
|
||||
case ModIndex_FM476: src_preset = preset_2fsk_476_async; break;
|
||||
default: src_preset = preset_ook_650_async; break;
|
||||
}
|
||||
|
||||
furi_hal_subghz_load_custom_preset(src_preset);
|
||||
furi_delay_ms(5);
|
||||
|
||||
uint32_t real_freq = furi_hal_subghz_set_frequency_and_path(app->frequency);
|
||||
FURI_LOG_I(TAG, "Capture: freq=%lu (requested %lu)", real_freq, app->frequency);
|
||||
furi_delay_ms(5);
|
||||
|
||||
furi_hal_subghz_rx();
|
||||
furi_delay_ms(50);
|
||||
float rssi_baseline = furi_hal_subghz_get_rssi();
|
||||
g_cap.rssi_baseline = rssi_baseline;
|
||||
FURI_LOG_I(TAG, "Capture: RSSI baseline=%.1f dBm", (double)rssi_baseline);
|
||||
|
||||
furi_hal_subghz_idle();
|
||||
furi_delay_ms(5);
|
||||
|
||||
cap_ctx_reset(&g_cap);
|
||||
|
||||
if(!app->signal_first.valid) {
|
||||
g_cap.target_first = true;
|
||||
app->signal_first.size = 0;
|
||||
app->signal_first.valid = false;
|
||||
FURI_LOG_I(TAG, "Capture target: FIRST signal");
|
||||
} else {
|
||||
g_cap.target_first = false;
|
||||
app->signal_second.size = 0;
|
||||
app->signal_second.valid = false;
|
||||
FURI_LOG_I(TAG, "Capture target: SECOND signal");
|
||||
}
|
||||
|
||||
g_cap.mod_index = app->mod_index;
|
||||
app->raw_capture_active = true;
|
||||
furi_hal_subghz_start_async_rx(capture_rx_callback, app);
|
||||
FURI_LOG_I(TAG, "Capture: RX STARTED");
|
||||
}
|
||||
|
||||
void rolljam_capture_stop(RollJamApp* app) {
|
||||
if(!app->raw_capture_active) {
|
||||
FURI_LOG_W(TAG, "Capture stop: was not active");
|
||||
return;
|
||||
}
|
||||
app->raw_capture_active = false;
|
||||
furi_hal_subghz_stop_async_rx();
|
||||
furi_delay_ms(5);
|
||||
FURI_LOG_I(TAG, "Capture stopped. cb=%lu state=%d valid=%d total=%d",
|
||||
g_cap.callback_count, g_cap.state, g_cap.valid_count, g_cap.total_count);
|
||||
FURI_LOG_I(TAG, " Sig1: size=%d valid=%d", app->signal_first.size, app->signal_first.valid);
|
||||
FURI_LOG_I(TAG, " Sig2: size=%d valid=%d", app->signal_second.size, app->signal_second.valid);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Validation
|
||||
// ============================================================
|
||||
|
||||
bool rolljam_signal_is_valid(RawSignal* signal) {
|
||||
if(g_cap.state != CapDone) {
|
||||
static int check_count = 0;
|
||||
check_count++;
|
||||
if(check_count % 10 == 0)
|
||||
FURI_LOG_D(TAG, "Validate: state=%d cb=%lu valid=%d total=%d size=%d",
|
||||
g_cap.state, g_cap.callback_count,
|
||||
g_cap.valid_count, g_cap.total_count, (int)signal->size);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(signal->size < (size_t)MIN_FRAME_PULSES) return false;
|
||||
|
||||
if(rolljam_is_jammer_pattern_mod(signal, g_cap.mod_index)) {
|
||||
signal->size = 0;
|
||||
cap_ctx_reset(&g_cap);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!rolljam_has_sufficient_variance(signal)) {
|
||||
signal->size = 0;
|
||||
cap_ctx_reset(&g_cap);
|
||||
return false;
|
||||
}
|
||||
|
||||
int good = 0;
|
||||
int total = (int)signal->size;
|
||||
for(int i = 0; i < total; i++) {
|
||||
int16_t abs_val = signal->data[i] > 0 ? signal->data[i] : -signal->data[i];
|
||||
if(abs_val >= MIN_PULSE_US) good++;
|
||||
}
|
||||
int ratio_pct = (total > 0) ? ((good * 100) / total) : 0;
|
||||
|
||||
if(ratio_pct > 50 && good >= MIN_FRAME_PULSES) {
|
||||
FURI_LOG_I(TAG, "Signal VALID: %d/%d (%d%%) size=%d", good, total, ratio_pct, total);
|
||||
return true;
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "Signal rejected: %d/%d (%d%%)", good, total, ratio_pct);
|
||||
signal->size = 0;
|
||||
cap_ctx_reset(&g_cap);
|
||||
return false;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Signal cleanup
|
||||
// ============================================================
|
||||
|
||||
void rolljam_signal_cleanup(RawSignal* signal) {
|
||||
if(signal->size < (size_t)MIN_FRAME_PULSES) return;
|
||||
|
||||
int16_t* cleaned = malloc(RAW_SIGNAL_MAX_SIZE * sizeof(int16_t));
|
||||
if(!cleaned) return;
|
||||
size_t out = 0;
|
||||
|
||||
size_t start = 0;
|
||||
while(start < signal->size) {
|
||||
int16_t abs_val = signal->data[start] > 0 ? signal->data[start] : -signal->data[start];
|
||||
if(abs_val >= MIN_PULSE_US) break;
|
||||
start++;
|
||||
}
|
||||
|
||||
for(size_t i = start; i < signal->size; i++) {
|
||||
int16_t val = signal->data[i];
|
||||
int16_t abs_val = val > 0 ? val : -val;
|
||||
bool is_positive = (val > 0);
|
||||
|
||||
if(abs_val < MIN_PULSE_US) {
|
||||
if(out > 0) {
|
||||
int16_t prev = cleaned[out - 1];
|
||||
bool prev_positive = (prev > 0);
|
||||
int16_t prev_abs = prev > 0 ? prev : -prev;
|
||||
if(prev_positive == is_positive) {
|
||||
int32_t merged = (int32_t)prev_abs + abs_val;
|
||||
if(merged > 32767) merged = 32767;
|
||||
cleaned[out - 1] = prev_positive ? (int16_t)merged : -(int16_t)merged;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
int32_t q = ((abs_val + 50) / 100) * 100;
|
||||
if(q < MIN_PULSE_US) q = MIN_PULSE_US;
|
||||
if(q > 32767) q = 32767;
|
||||
|
||||
if(out < RAW_SIGNAL_MAX_SIZE)
|
||||
cleaned[out++] = is_positive ? (int16_t)q : -(int16_t)q;
|
||||
}
|
||||
|
||||
while(out > 0) {
|
||||
int16_t abs_last = cleaned[out-1] > 0 ? cleaned[out-1] : -cleaned[out-1];
|
||||
if(abs_last >= MIN_PULSE_US && abs_last < 32767) break;
|
||||
out--;
|
||||
}
|
||||
|
||||
if(out >= (size_t)MIN_FRAME_PULSES) {
|
||||
size_t orig = signal->size;
|
||||
memcpy(signal->data, cleaned, out * sizeof(int16_t));
|
||||
signal->size = out;
|
||||
FURI_LOG_I(TAG, "Cleanup: %d -> %d samples", (int)orig, (int)out);
|
||||
}
|
||||
free(cleaned);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// TX
|
||||
// ============================================================
|
||||
|
||||
typedef struct {
|
||||
const int16_t* data;
|
||||
size_t size;
|
||||
volatile size_t index;
|
||||
} TxCtx;
|
||||
|
||||
static TxCtx g_tx;
|
||||
|
||||
static LevelDuration tx_feed(void* context) {
|
||||
UNUSED(context);
|
||||
if(g_tx.index >= g_tx.size) return level_duration_reset();
|
||||
int16_t sample = g_tx.data[g_tx.index++];
|
||||
bool level = (sample > 0);
|
||||
uint32_t dur = (uint32_t)(sample > 0 ? sample : -sample);
|
||||
return level_duration_make(level, dur);
|
||||
}
|
||||
|
||||
void rolljam_transmit_signal(RollJamApp* app, RawSignal* signal) {
|
||||
if(!signal->valid || signal->size == 0) {
|
||||
FURI_LOG_E(TAG, "TX: no valid signal");
|
||||
return;
|
||||
}
|
||||
FURI_LOG_I(TAG, "TX: %d samples at %lu Hz (3x)", (int)signal->size, app->frequency);
|
||||
|
||||
const uint8_t* tx_src;
|
||||
switch(app->mod_index) {
|
||||
case ModIndex_FM238: tx_src = preset_fsk_tx_238; break;
|
||||
case ModIndex_FM476: tx_src = preset_fsk_tx_476; break;
|
||||
default: tx_src = preset_ook_tx; break;
|
||||
}
|
||||
furi_hal_subghz_load_custom_preset(tx_src);
|
||||
uint32_t real_freq = furi_hal_subghz_set_frequency_and_path(app->frequency);
|
||||
FURI_LOG_I(TAG, "TX: freq=%lu", real_freq);
|
||||
furi_hal_subghz_idle();
|
||||
furi_delay_ms(5);
|
||||
|
||||
for(int tx_repeat = 0; tx_repeat < 3; tx_repeat++) {
|
||||
g_tx.data = signal->data;
|
||||
g_tx.size = signal->size;
|
||||
g_tx.index = 0;
|
||||
|
||||
if(!furi_hal_subghz_start_async_tx(tx_feed, NULL)) {
|
||||
FURI_LOG_E(TAG, "TX: start failed on repeat %d!", tx_repeat);
|
||||
furi_hal_subghz_idle();
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t timeout = 0;
|
||||
while(!furi_hal_subghz_is_async_tx_complete()) {
|
||||
furi_delay_ms(5);
|
||||
if(++timeout > 2000) {
|
||||
FURI_LOG_E(TAG, "TX: timeout on repeat %d!", tx_repeat);
|
||||
break;
|
||||
}
|
||||
}
|
||||
furi_hal_subghz_stop_async_tx();
|
||||
FURI_LOG_I(TAG, "TX: repeat %d done (%d/%d)",
|
||||
tx_repeat, (int)g_tx.index, (int)signal->size);
|
||||
if(tx_repeat < 2) furi_delay_ms(50);
|
||||
}
|
||||
furi_hal_subghz_idle();
|
||||
FURI_LOG_I(TAG, "TX: all repeats done");
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Save
|
||||
// ============================================================
|
||||
|
||||
void rolljam_save_signal(RollJamApp* app, RawSignal* signal) {
|
||||
if(!signal->valid || signal->size == 0) {
|
||||
FURI_LOG_E(TAG, "Save: no signal");
|
||||
return;
|
||||
}
|
||||
|
||||
DateTime dt;
|
||||
furi_hal_rtc_get_datetime(&dt);
|
||||
|
||||
FuriString* path = furi_string_alloc_printf(
|
||||
"/ext/subghz/RJ_%04d%02d%02d_%02d%02d%02d.sub",
|
||||
dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second);
|
||||
|
||||
FURI_LOG_I(TAG, "Saving: %s", furi_string_get_cstr(path));
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
storage_simply_mkdir(storage, "/ext/subghz");
|
||||
File* file = storage_file_alloc(storage);
|
||||
|
||||
if(storage_file_open(file, furi_string_get_cstr(path), FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
|
||||
FuriString* line = furi_string_alloc();
|
||||
|
||||
furi_string_set(line, "Filetype: Flipper SubGhz RAW File\n");
|
||||
storage_file_write(file, furi_string_get_cstr(line), furi_string_size(line));
|
||||
furi_string_printf(line, "Version: 1\n");
|
||||
storage_file_write(file, furi_string_get_cstr(line), furi_string_size(line));
|
||||
furi_string_printf(line, "Frequency: %lu\n", app->frequency);
|
||||
storage_file_write(file, furi_string_get_cstr(line), furi_string_size(line));
|
||||
|
||||
const char* pname;
|
||||
switch(app->mod_index) {
|
||||
case ModIndex_AM270: pname = "FuriHalSubGhzPresetOok270Async"; break;
|
||||
case ModIndex_FM238: pname = "FuriHalSubGhzPreset2FSKDev238Async"; break;
|
||||
case ModIndex_FM476: pname = "FuriHalSubGhzPreset2FSKDev476Async"; break;
|
||||
default: pname = "FuriHalSubGhzPresetOok650Async"; break;
|
||||
}
|
||||
furi_string_printf(line, "Preset: %s\n", pname);
|
||||
storage_file_write(file, furi_string_get_cstr(line), furi_string_size(line));
|
||||
furi_string_printf(line, "Protocol: RAW\n");
|
||||
storage_file_write(file, furi_string_get_cstr(line), furi_string_size(line));
|
||||
|
||||
size_t i = 0;
|
||||
while(i < signal->size) {
|
||||
furi_string_set(line, "RAW_Data:");
|
||||
size_t end = i + 512;
|
||||
if(end > signal->size) end = signal->size;
|
||||
for(; i < end; i++)
|
||||
furi_string_cat_printf(line, " %d", signal->data[i]);
|
||||
furi_string_cat(line, "\n");
|
||||
storage_file_write(file, furi_string_get_cstr(line), furi_string_size(line));
|
||||
}
|
||||
furi_string_free(line);
|
||||
FURI_LOG_I(TAG, "Saved: %d samples", (int)signal->size);
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Save failed!");
|
||||
}
|
||||
|
||||
storage_file_close(file);
|
||||
storage_file_free(file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
furi_string_free(path);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../rolljam.h"
|
||||
|
||||
/*
|
||||
* Internal CC1101 raw signal capture and transmission.
|
||||
*
|
||||
* Capture: uses narrow RX bandwidth so the offset jamming
|
||||
* from the external CC1101 is filtered out.
|
||||
*
|
||||
* The captured raw data is stored as signed int16 values:
|
||||
* positive = high-level duration (microseconds)
|
||||
* negative = low-level duration (microseconds)
|
||||
*
|
||||
* This matches the Flipper .sub RAW format.
|
||||
*/
|
||||
|
||||
void rolljam_capture_start(RollJamApp* app);
|
||||
void rolljam_capture_stop(RollJamApp* app);
|
||||
|
||||
bool rolljam_signal_is_valid(RawSignal* signal);
|
||||
|
||||
void rolljam_signal_cleanup(RawSignal* signal);
|
||||
void rolljam_transmit_signal(RollJamApp* app, RawSignal* signal);
|
||||
void rolljam_save_signal(RollJamApp* app, RawSignal* signal);
|
||||
@@ -1,21 +0,0 @@
|
||||
applications_user/rolljam/
|
||||
├── application.fam
|
||||
├── rolljam.png (icon 10x10)
|
||||
├── rolljam.c
|
||||
├── rolljam_icons.h
|
||||
├── scenes/
|
||||
│ ├── rolljam_scene.h
|
||||
│ ├── rolljam_scene_config.h
|
||||
│ ├── rolljam_scene_menu.c
|
||||
│ ├── rolljam_scene_attack_phase1.c
|
||||
│ ├── rolljam_scene_attack_phase2.c
|
||||
│ ├── rolljam_scene_attack_phase3.c
|
||||
│ └── rolljam_scene_result.c
|
||||
├── helpers/
|
||||
│ ├── rolljam_cc1101_ext.h
|
||||
│ ├── rolljam_cc1101_ext.c
|
||||
│ ├── rolljam_receiver.h
|
||||
│ └── rolljam_receiver.c
|
||||
└── views/
|
||||
├── rolljam_attack_view.h
|
||||
└── rolljam_attack_view.c
|
||||
@@ -1,232 +0,0 @@
|
||||
#include "rolljam.h"
|
||||
#include "scenes/rolljam_scene.h"
|
||||
#include "helpers/rolljam_cc1101_ext.h"
|
||||
#include "helpers/rolljam_receiver.h"
|
||||
#include "helpers/rolljam_cc1101_ext.h"
|
||||
|
||||
// ============================================================
|
||||
// Frequency / modulation tables
|
||||
// ============================================================
|
||||
|
||||
const uint32_t freq_values[] = {
|
||||
300000000,
|
||||
303875000,
|
||||
315000000,
|
||||
318000000,
|
||||
390000000,
|
||||
433075000,
|
||||
433920000,
|
||||
434420000,
|
||||
438900000,
|
||||
868350000,
|
||||
915000000,
|
||||
};
|
||||
|
||||
const char* freq_names[] = {
|
||||
"300.00",
|
||||
"303.87",
|
||||
"315.00",
|
||||
"318.00",
|
||||
"390.00",
|
||||
"433.07",
|
||||
"433.92",
|
||||
"434.42",
|
||||
"438.90",
|
||||
"868.35",
|
||||
"915.00",
|
||||
};
|
||||
|
||||
const char* mod_names[] = {
|
||||
"AM 650",
|
||||
"AM 270",
|
||||
"FM 238",
|
||||
"FM 476",
|
||||
};
|
||||
|
||||
const uint32_t jam_offset_values[] = {
|
||||
300000,
|
||||
500000,
|
||||
700000,
|
||||
1000000,
|
||||
};
|
||||
|
||||
const char* jam_offset_names[] = {
|
||||
"300 kHz",
|
||||
"500 kHz",
|
||||
"700 kHz",
|
||||
"1000 kHz",
|
||||
};
|
||||
|
||||
const char* hw_names[] = {
|
||||
"CC1101",
|
||||
"Flux Cap",
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
// Scene handlers table (extern declarations in scene header)
|
||||
// ============================================================
|
||||
|
||||
void (*const rolljam_scene_on_enter_handlers[])(void*) = {
|
||||
rolljam_scene_menu_on_enter,
|
||||
rolljam_scene_attack_phase1_on_enter,
|
||||
rolljam_scene_attack_phase2_on_enter,
|
||||
rolljam_scene_attack_phase3_on_enter,
|
||||
rolljam_scene_result_on_enter,
|
||||
};
|
||||
|
||||
bool (*const rolljam_scene_on_event_handlers[])(void*, SceneManagerEvent) = {
|
||||
rolljam_scene_menu_on_event,
|
||||
rolljam_scene_attack_phase1_on_event,
|
||||
rolljam_scene_attack_phase2_on_event,
|
||||
rolljam_scene_attack_phase3_on_event,
|
||||
rolljam_scene_result_on_event,
|
||||
};
|
||||
|
||||
void (*const rolljam_scene_on_exit_handlers[])(void*) = {
|
||||
rolljam_scene_menu_on_exit,
|
||||
rolljam_scene_attack_phase1_on_exit,
|
||||
rolljam_scene_attack_phase2_on_exit,
|
||||
rolljam_scene_attack_phase3_on_exit,
|
||||
rolljam_scene_result_on_exit,
|
||||
};
|
||||
|
||||
const SceneManagerHandlers rolljam_scene_handlers = {
|
||||
.on_enter_handlers = rolljam_scene_on_enter_handlers,
|
||||
.on_event_handlers = rolljam_scene_on_event_handlers,
|
||||
.on_exit_handlers = rolljam_scene_on_exit_handlers,
|
||||
.scene_num = RollJamSceneCount,
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
// Navigation callbacks
|
||||
// ============================================================
|
||||
|
||||
static bool rolljam_navigation_callback(void* context) {
|
||||
RollJamApp* app = context;
|
||||
return scene_manager_handle_back_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static bool rolljam_custom_event_callback(void* context, uint32_t event) {
|
||||
RollJamApp* app = context;
|
||||
return scene_manager_handle_custom_event(app->scene_manager, event);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// App alloc
|
||||
// ============================================================
|
||||
|
||||
static RollJamApp* rolljam_app_alloc(void) {
|
||||
RollJamApp* app = malloc(sizeof(RollJamApp));
|
||||
memset(app, 0, sizeof(RollJamApp));
|
||||
|
||||
app->freq_index = FreqIndex_433_92;
|
||||
app->frequency = freq_values[FreqIndex_433_92];
|
||||
app->mod_index = ModIndex_AM650;
|
||||
app->jam_offset_index = JamOffIndex_700k;
|
||||
app->jam_offset_hz = jam_offset_values[JamOffIndex_700k];
|
||||
app->hw_index = HwIndex_CC1101;
|
||||
|
||||
// Services
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
app->notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
app->storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
// Scene manager
|
||||
app->scene_manager = scene_manager_alloc(&rolljam_scene_handlers, app);
|
||||
|
||||
// View dispatcher
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||
view_dispatcher_set_custom_event_callback(
|
||||
app->view_dispatcher, rolljam_custom_event_callback);
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
app->view_dispatcher, rolljam_navigation_callback);
|
||||
view_dispatcher_attach_to_gui(
|
||||
app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
// Variable item list
|
||||
app->var_item_list = variable_item_list_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
RollJamViewVarItemList,
|
||||
variable_item_list_get_view(app->var_item_list));
|
||||
|
||||
// Widget
|
||||
app->widget = widget_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
RollJamViewWidget,
|
||||
widget_get_view(app->widget));
|
||||
|
||||
// Dialog
|
||||
app->dialog_ex = dialog_ex_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
RollJamViewDialogEx,
|
||||
dialog_ex_get_view(app->dialog_ex));
|
||||
|
||||
// Popup
|
||||
app->popup = popup_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
RollJamViewPopup,
|
||||
popup_get_view(app->popup));
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// App free
|
||||
// ============================================================
|
||||
|
||||
static void rolljam_app_free(RollJamApp* app) {
|
||||
if(app->jamming_active) {
|
||||
rolljam_jammer_stop(app);
|
||||
}
|
||||
if(app->raw_capture_active) {
|
||||
rolljam_capture_stop(app);
|
||||
}
|
||||
|
||||
view_dispatcher_remove_view(app->view_dispatcher, RollJamViewVarItemList);
|
||||
variable_item_list_free(app->var_item_list);
|
||||
|
||||
view_dispatcher_remove_view(app->view_dispatcher, RollJamViewWidget);
|
||||
widget_free(app->widget);
|
||||
|
||||
view_dispatcher_remove_view(app->view_dispatcher, RollJamViewDialogEx);
|
||||
dialog_ex_free(app->dialog_ex);
|
||||
|
||||
view_dispatcher_remove_view(app->view_dispatcher, RollJamViewPopup);
|
||||
popup_free(app->popup);
|
||||
|
||||
scene_manager_free(app->scene_manager);
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
|
||||
furi_record_close(RECORD_GUI);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
free(app);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Entry point
|
||||
// ============================================================
|
||||
|
||||
int32_t rolljam_app(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
RollJamApp* app = rolljam_app_alloc();
|
||||
|
||||
FURI_LOG_I(TAG, "=== RollJam Started ===");
|
||||
FURI_LOG_I(TAG, "Internal CC1101 = RX capture (narrow BW)");
|
||||
FURI_LOG_I(TAG, "External CC1101 = TX jam (offset +%lu Hz)", app->jam_offset_hz);
|
||||
|
||||
scene_manager_next_scene(app->scene_manager, RollJamSceneMenu);
|
||||
view_dispatcher_run(app->view_dispatcher);
|
||||
|
||||
rolljam_app_free(app);
|
||||
|
||||
FURI_LOG_I(TAG, "=== RollJam Stopped ===");
|
||||
return 0;
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/modules/popup.h>
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
#include <gui/modules/widget.h>
|
||||
#include <gui/modules/dialog_ex.h>
|
||||
#include <notification/notification.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <storage/storage.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define TAG "RollJam"
|
||||
|
||||
#define RAW_SIGNAL_MAX_SIZE 4096
|
||||
|
||||
// ============================================================
|
||||
// Frequencies
|
||||
// ============================================================
|
||||
typedef enum {
|
||||
FreqIndex_300_00 = 0,
|
||||
FreqIndex_303_87,
|
||||
FreqIndex_315_00,
|
||||
FreqIndex_318_00,
|
||||
FreqIndex_390_00,
|
||||
FreqIndex_433_07,
|
||||
FreqIndex_433_92,
|
||||
FreqIndex_434_42,
|
||||
FreqIndex_438_90,
|
||||
FreqIndex_868_35,
|
||||
FreqIndex_915_00,
|
||||
FreqIndex_COUNT,
|
||||
} FreqIndex;
|
||||
|
||||
extern const uint32_t freq_values[];
|
||||
extern const char* freq_names[];
|
||||
|
||||
// ============================================================
|
||||
// Modulations
|
||||
// ============================================================
|
||||
typedef enum {
|
||||
ModIndex_AM650 = 0,
|
||||
ModIndex_AM270,
|
||||
ModIndex_FM238,
|
||||
ModIndex_FM476,
|
||||
ModIndex_COUNT,
|
||||
} ModIndex;
|
||||
|
||||
extern const char* mod_names[];
|
||||
|
||||
// ============================================================
|
||||
// Jam offsets
|
||||
// ============================================================
|
||||
typedef enum {
|
||||
JamOffIndex_300k = 0,
|
||||
JamOffIndex_500k,
|
||||
JamOffIndex_700k,
|
||||
JamOffIndex_1000k,
|
||||
JamOffIndex_COUNT,
|
||||
} JamOffIndex;
|
||||
|
||||
extern const uint32_t jam_offset_values[];
|
||||
extern const char* jam_offset_names[];
|
||||
|
||||
// ============================================================
|
||||
// Hardware type
|
||||
// ============================================================
|
||||
typedef enum {
|
||||
HwIndex_CC1101 = 0,
|
||||
HwIndex_FluxCapacitor,
|
||||
HwIndex_COUNT,
|
||||
} HwIndex;
|
||||
|
||||
extern const char* hw_names[];
|
||||
|
||||
// ============================================================
|
||||
// Scenes
|
||||
// ============================================================
|
||||
typedef enum {
|
||||
RollJamSceneMenu,
|
||||
RollJamSceneAttackPhase1,
|
||||
RollJamSceneAttackPhase2,
|
||||
RollJamSceneAttackPhase3,
|
||||
RollJamSceneResult,
|
||||
RollJamSceneCount,
|
||||
} RollJamScene;
|
||||
|
||||
// ============================================================
|
||||
// Views
|
||||
// ============================================================
|
||||
typedef enum {
|
||||
RollJamViewVarItemList,
|
||||
RollJamViewWidget,
|
||||
RollJamViewDialogEx,
|
||||
RollJamViewPopup,
|
||||
} RollJamView;
|
||||
|
||||
// ============================================================
|
||||
// Custom events
|
||||
// ============================================================
|
||||
typedef enum {
|
||||
RollJamEventStartAttack = 100,
|
||||
RollJamEventSignalCaptured,
|
||||
RollJamEventPhase3Done,
|
||||
RollJamEventReplayNow,
|
||||
RollJamEventSaveSignal,
|
||||
RollJamEventBack,
|
||||
} RollJamEvent;
|
||||
|
||||
// ============================================================
|
||||
// Raw signal container
|
||||
// ============================================================
|
||||
typedef struct {
|
||||
int16_t data[RAW_SIGNAL_MAX_SIZE];
|
||||
size_t size;
|
||||
bool valid;
|
||||
} RawSignal;
|
||||
|
||||
// ============================================================
|
||||
// Main app struct
|
||||
// ============================================================
|
||||
typedef struct {
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
SceneManager* scene_manager;
|
||||
NotificationApp* notification;
|
||||
Storage* storage;
|
||||
|
||||
VariableItemList* var_item_list;
|
||||
Widget* widget;
|
||||
DialogEx* dialog_ex;
|
||||
Popup* popup;
|
||||
|
||||
FreqIndex freq_index;
|
||||
ModIndex mod_index;
|
||||
JamOffIndex jam_offset_index;
|
||||
HwIndex hw_index;
|
||||
uint32_t frequency;
|
||||
uint32_t jam_frequency;
|
||||
uint32_t jam_offset_hz;
|
||||
|
||||
RawSignal signal_first;
|
||||
RawSignal signal_second;
|
||||
|
||||
bool jamming_active;
|
||||
FuriThread* jam_thread;
|
||||
volatile bool jam_thread_running;
|
||||
|
||||
volatile bool raw_capture_active;
|
||||
|
||||
|
||||
} RollJamApp;
|
||||
@@ -1,9 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Icon assets are auto-generated by the build system
|
||||
// from the images/ folder. If no custom icons are needed,
|
||||
// this file can remain minimal.
|
||||
|
||||
// If you place .png files in an images/ folder,
|
||||
// the build system generates icon references automatically.
|
||||
// Access them via &I_iconname
|
||||
@@ -1,27 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../rolljam.h"
|
||||
|
||||
// Scene on_enter
|
||||
void rolljam_scene_menu_on_enter(void* context);
|
||||
void rolljam_scene_attack_phase1_on_enter(void* context);
|
||||
void rolljam_scene_attack_phase2_on_enter(void* context);
|
||||
void rolljam_scene_attack_phase3_on_enter(void* context);
|
||||
void rolljam_scene_result_on_enter(void* context);
|
||||
|
||||
// Scene on_event
|
||||
bool rolljam_scene_menu_on_event(void* context, SceneManagerEvent event);
|
||||
bool rolljam_scene_attack_phase1_on_event(void* context, SceneManagerEvent event);
|
||||
bool rolljam_scene_attack_phase2_on_event(void* context, SceneManagerEvent event);
|
||||
bool rolljam_scene_attack_phase3_on_event(void* context, SceneManagerEvent event);
|
||||
bool rolljam_scene_result_on_event(void* context, SceneManagerEvent event);
|
||||
|
||||
// Scene on_exit
|
||||
void rolljam_scene_menu_on_exit(void* context);
|
||||
void rolljam_scene_attack_phase1_on_exit(void* context);
|
||||
void rolljam_scene_attack_phase2_on_exit(void* context);
|
||||
void rolljam_scene_attack_phase3_on_exit(void* context);
|
||||
void rolljam_scene_result_on_exit(void* context);
|
||||
|
||||
// Scene manager handlers (defined in rolljam.c)
|
||||
extern const SceneManagerHandlers rolljam_scene_handlers;
|
||||
@@ -1,126 +0,0 @@
|
||||
#include "rolljam_scene.h"
|
||||
#include "../helpers/rolljam_cc1101_ext.h"
|
||||
#include "../helpers/rolljam_receiver.h"
|
||||
|
||||
// ============================================================
|
||||
// Phase 1: JAM + CAPTURE first keyfob press
|
||||
// ============================================================
|
||||
|
||||
static void phase1_timer_callback(void* context) {
|
||||
RollJamApp* app = context;
|
||||
|
||||
if(app->signal_first.size >= 20 &&
|
||||
rolljam_signal_is_valid(&app->signal_first)) {
|
||||
view_dispatcher_send_custom_event(
|
||||
app->view_dispatcher, RollJamEventSignalCaptured);
|
||||
}
|
||||
}
|
||||
|
||||
void rolljam_scene_attack_phase1_on_enter(void* context) {
|
||||
RollJamApp* app = context;
|
||||
|
||||
widget_reset(app->widget);
|
||||
widget_add_string_element(
|
||||
app->widget, 64, 2, AlignCenter, AlignTop,
|
||||
FontPrimary, "PHASE 1 / 4");
|
||||
widget_add_string_element(
|
||||
app->widget, 64, 16, AlignCenter, AlignTop,
|
||||
FontSecondary, "Starting...");
|
||||
widget_add_string_element(
|
||||
app->widget, 64, 56, AlignCenter, AlignTop,
|
||||
FontSecondary, "[BACK] cancel");
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, RollJamViewWidget);
|
||||
|
||||
rolljam_ext_set_flux_capacitor(app->hw_index == HwIndex_FluxCapacitor);
|
||||
|
||||
rolljam_jammer_start(app);
|
||||
furi_delay_ms(300);
|
||||
|
||||
widget_reset(app->widget);
|
||||
widget_add_string_element(
|
||||
app->widget, 64, 2, AlignCenter, AlignTop,
|
||||
FontPrimary, "PHASE 1 / 4");
|
||||
if(app->jamming_active) {
|
||||
widget_add_string_element(
|
||||
app->widget, 64, 16, AlignCenter, AlignTop,
|
||||
FontSecondary, "Jamming active...");
|
||||
FURI_LOG_I(TAG, "Phase1: jammer activo en %lu Hz", app->jam_frequency);
|
||||
} else {
|
||||
widget_add_string_element(
|
||||
app->widget, 64, 16, AlignCenter, AlignTop,
|
||||
FontSecondary, "No ext jammer");
|
||||
FURI_LOG_W(TAG, "Phase1: sin jammer, capturando de todas formas");
|
||||
}
|
||||
widget_add_string_element(
|
||||
app->widget, 64, 28, AlignCenter, AlignTop,
|
||||
FontSecondary, "Listening for keyfob");
|
||||
widget_add_string_element(
|
||||
app->widget, 64, 42, AlignCenter, AlignTop,
|
||||
FontPrimary, "PRESS KEYFOB NOW");
|
||||
widget_add_string_element(
|
||||
app->widget, 64, 56, AlignCenter, AlignTop,
|
||||
FontSecondary, "[BACK] cancel");
|
||||
|
||||
rolljam_capture_start(app);
|
||||
|
||||
notification_message(app->notification, &sequence_blink_blue_100);
|
||||
|
||||
FuriTimer* timer = furi_timer_alloc(
|
||||
phase1_timer_callback, FuriTimerTypePeriodic, app);
|
||||
furi_timer_start(timer, 300);
|
||||
|
||||
scene_manager_set_scene_state(
|
||||
app->scene_manager, RollJamSceneAttackPhase1, (uint32_t)timer);
|
||||
|
||||
FURI_LOG_I(TAG, "Phase1: waiting for 1st keyfob press...");
|
||||
}
|
||||
|
||||
bool rolljam_scene_attack_phase1_on_event(void* context, SceneManagerEvent event) {
|
||||
RollJamApp* app = context;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == RollJamEventSignalCaptured) {
|
||||
rolljam_capture_stop(app);
|
||||
|
||||
if(!rolljam_signal_is_valid(&app->signal_first)) {
|
||||
FURI_LOG_W(TAG, "Phase1: false capture, restarting RX...");
|
||||
app->signal_first.size = 0;
|
||||
app->signal_first.valid = false;
|
||||
furi_delay_ms(50);
|
||||
rolljam_capture_start(app);
|
||||
return true;
|
||||
}
|
||||
|
||||
rolljam_signal_cleanup(&app->signal_first);
|
||||
app->signal_first.valid = true;
|
||||
|
||||
notification_message(app->notification, &sequence_success);
|
||||
FURI_LOG_I(TAG, "Phase1: 1st signal captured! size=%d",
|
||||
(int)app->signal_first.size);
|
||||
|
||||
scene_manager_next_scene(app->scene_manager, RollJamSceneAttackPhase2);
|
||||
return true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
FURI_LOG_I(TAG, "Phase1: cancelled");
|
||||
rolljam_capture_stop(app);
|
||||
rolljam_jammer_stop(app);
|
||||
scene_manager_search_and_switch_to_another_scene(
|
||||
app->scene_manager, RollJamSceneMenu);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void rolljam_scene_attack_phase1_on_exit(void* context) {
|
||||
RollJamApp* app = context;
|
||||
|
||||
FuriTimer* timer = (FuriTimer*)scene_manager_get_scene_state(
|
||||
app->scene_manager, RollJamSceneAttackPhase1);
|
||||
if(timer) {
|
||||
furi_timer_stop(timer);
|
||||
furi_timer_free(timer);
|
||||
}
|
||||
|
||||
widget_reset(app->widget);
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
#include "rolljam_scene.h"
|
||||
#include "../helpers/rolljam_cc1101_ext.h"
|
||||
#include "../helpers/rolljam_receiver.h"
|
||||
|
||||
// ============================================================
|
||||
// Phase 2: JAM + CAPTURE second keyfob press
|
||||
// ============================================================
|
||||
|
||||
static void phase2_timer_callback(void* context) {
|
||||
RollJamApp* app = context;
|
||||
|
||||
if(app->signal_second.size >= 20 &&
|
||||
rolljam_signal_is_valid(&app->signal_second)) {
|
||||
view_dispatcher_send_custom_event(
|
||||
app->view_dispatcher, RollJamEventSignalCaptured);
|
||||
}
|
||||
}
|
||||
|
||||
void rolljam_scene_attack_phase2_on_enter(void* context) {
|
||||
RollJamApp* app = context;
|
||||
|
||||
widget_reset(app->widget);
|
||||
widget_add_string_element(
|
||||
app->widget, 64, 2, AlignCenter, AlignTop,
|
||||
FontPrimary, "PHASE 2 / 4");
|
||||
widget_add_string_element(
|
||||
app->widget, 64, 16, AlignCenter, AlignTop,
|
||||
FontSecondary, "1st code CAPTURED!");
|
||||
widget_add_string_element(
|
||||
app->widget, 64, 28, AlignCenter, AlignTop,
|
||||
FontSecondary, "Still jamming...");
|
||||
widget_add_string_element(
|
||||
app->widget, 64, 42, AlignCenter, AlignTop,
|
||||
FontPrimary, "PRESS KEYFOB AGAIN");
|
||||
widget_add_string_element(
|
||||
app->widget, 64, 56, AlignCenter, AlignTop,
|
||||
FontSecondary, "[BACK] cancel");
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, RollJamViewWidget);
|
||||
|
||||
memset(app->signal_second.data, 0, sizeof(app->signal_second.data));
|
||||
app->signal_second.size = 0;
|
||||
app->signal_second.valid = false;
|
||||
|
||||
rolljam_capture_stop(app);
|
||||
furi_delay_ms(50);
|
||||
rolljam_capture_start(app);
|
||||
|
||||
notification_message(app->notification, &sequence_blink_yellow_100);
|
||||
|
||||
FuriTimer* timer = furi_timer_alloc(
|
||||
phase2_timer_callback, FuriTimerTypePeriodic, app);
|
||||
furi_timer_start(timer, 300);
|
||||
|
||||
scene_manager_set_scene_state(
|
||||
app->scene_manager, RollJamSceneAttackPhase2, (uint32_t)timer);
|
||||
|
||||
FURI_LOG_I(TAG, "Phase2: waiting for 2nd keyfob press...");
|
||||
}
|
||||
|
||||
bool rolljam_scene_attack_phase2_on_event(void* context, SceneManagerEvent event) {
|
||||
RollJamApp* app = context;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == RollJamEventSignalCaptured) {
|
||||
rolljam_capture_stop(app);
|
||||
|
||||
if(!rolljam_signal_is_valid(&app->signal_second)) {
|
||||
FURI_LOG_W(TAG, "Phase2: false capture, restarting RX...");
|
||||
app->signal_second.size = 0;
|
||||
app->signal_second.valid = false;
|
||||
furi_delay_ms(50);
|
||||
rolljam_capture_start(app);
|
||||
return true;
|
||||
}
|
||||
|
||||
rolljam_signal_cleanup(&app->signal_second);
|
||||
app->signal_second.valid = true;
|
||||
|
||||
notification_message(app->notification, &sequence_success);
|
||||
FURI_LOG_I(TAG, "Phase2: 2nd signal captured! size=%d",
|
||||
(int)app->signal_second.size);
|
||||
|
||||
rolljam_capture_stop(app);
|
||||
scene_manager_next_scene(app->scene_manager, RollJamSceneAttackPhase3);
|
||||
return true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
FURI_LOG_I(TAG, "Phase2: cancelled");
|
||||
rolljam_capture_stop(app);
|
||||
rolljam_jammer_stop(app);
|
||||
scene_manager_search_and_switch_to_another_scene(
|
||||
app->scene_manager, RollJamSceneMenu);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void rolljam_scene_attack_phase2_on_exit(void* context) {
|
||||
RollJamApp* app = context;
|
||||
|
||||
FuriTimer* timer = (FuriTimer*)scene_manager_get_scene_state(
|
||||
app->scene_manager, RollJamSceneAttackPhase2);
|
||||
if(timer) {
|
||||
furi_timer_stop(timer);
|
||||
furi_timer_free(timer);
|
||||
}
|
||||
|
||||
widget_reset(app->widget);
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
#include "rolljam_scene.h"
|
||||
#include "../helpers/rolljam_cc1101_ext.h"
|
||||
#include "../helpers/rolljam_receiver.h"
|
||||
|
||||
// ============================================================
|
||||
// Phase 3: STOP jam + REPLAY first signal
|
||||
// The victim device opens. We keep the 2nd (newer) code.
|
||||
// ============================================================
|
||||
|
||||
void rolljam_scene_attack_phase3_on_enter(void* context) {
|
||||
RollJamApp* app = context;
|
||||
|
||||
widget_reset(app->widget);
|
||||
widget_add_string_element(
|
||||
app->widget, 64, 2, AlignCenter, AlignTop,
|
||||
FontPrimary, "PHASE 3 / 4");
|
||||
widget_add_string_element(
|
||||
app->widget, 64, 18, AlignCenter, AlignTop,
|
||||
FontSecondary, "Stopping jammer...");
|
||||
widget_add_string_element(
|
||||
app->widget, 64, 32, AlignCenter, AlignTop,
|
||||
FontPrimary, "REPLAYING 1st CODE");
|
||||
widget_add_string_element(
|
||||
app->widget, 64, 48, AlignCenter, AlignTop,
|
||||
FontSecondary, "Target should open!");
|
||||
|
||||
view_dispatcher_switch_to_view(
|
||||
app->view_dispatcher, RollJamViewWidget);
|
||||
|
||||
notification_message(app->notification, &sequence_blink_green_100);
|
||||
|
||||
rolljam_jammer_stop(app);
|
||||
|
||||
furi_delay_ms(1000);
|
||||
|
||||
rolljam_transmit_signal(app, &app->signal_first);
|
||||
|
||||
FURI_LOG_I(TAG, "Phase3: 1st code replayed. Keeping 2nd code.");
|
||||
|
||||
notification_message(app->notification, &sequence_success);
|
||||
|
||||
furi_delay_ms(800);
|
||||
|
||||
view_dispatcher_send_custom_event(
|
||||
app->view_dispatcher, RollJamEventPhase3Done);
|
||||
}
|
||||
|
||||
bool rolljam_scene_attack_phase3_on_event(void* context, SceneManagerEvent event) {
|
||||
RollJamApp* app = context;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == RollJamEventPhase3Done) {
|
||||
scene_manager_next_scene(
|
||||
app->scene_manager, RollJamSceneResult);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void rolljam_scene_attack_phase3_on_exit(void* context) {
|
||||
RollJamApp* app = context;
|
||||
widget_reset(app->widget);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* Scene configuration file.
|
||||
* Lists all scenes for the SceneManager.
|
||||
*
|
||||
* In some Flipper apps this uses ADD_SCENE macros.
|
||||
* We handle it manually via the handlers arrays in rolljam.c
|
||||
* so this file just documents the scene list.
|
||||
*
|
||||
* Scenes:
|
||||
* 0 - RollJamSceneMenu
|
||||
* 1 - RollJamSceneAttackPhase1
|
||||
* 2 - RollJamSceneAttackPhase2
|
||||
* 3 - RollJamSceneAttackPhase3
|
||||
* 4 - RollJamSceneResult
|
||||
*/
|
||||
@@ -1,161 +0,0 @@
|
||||
#include "rolljam_scene.h"
|
||||
|
||||
// ============================================================
|
||||
// Menu scene: select frequency, modulation, start attack
|
||||
// ============================================================
|
||||
|
||||
static uint8_t get_min_offset_index(uint8_t mod_index) {
|
||||
if(mod_index == ModIndex_AM270) return JamOffIndex_1000k;
|
||||
return JamOffIndex_300k;
|
||||
}
|
||||
|
||||
static void enforce_min_offset(RollJamApp* app, VariableItem* offset_item) {
|
||||
uint8_t min_idx = get_min_offset_index(app->mod_index);
|
||||
if(app->jam_offset_index < min_idx) {
|
||||
app->jam_offset_index = min_idx;
|
||||
app->jam_offset_hz = jam_offset_values[min_idx];
|
||||
if(offset_item) {
|
||||
variable_item_set_current_value_index(offset_item, min_idx);
|
||||
variable_item_set_current_value_text(offset_item, jam_offset_names[min_idx]);
|
||||
}
|
||||
FURI_LOG_I(TAG, "Menu: offset ajustado a %s para AM270",
|
||||
jam_offset_names[min_idx]);
|
||||
}
|
||||
}
|
||||
|
||||
static VariableItem* s_offset_item = NULL;
|
||||
|
||||
static void menu_freq_changed(VariableItem* item) {
|
||||
RollJamApp* app = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
app->freq_index = index;
|
||||
app->frequency = freq_values[index];
|
||||
variable_item_set_current_value_text(item, freq_names[index]);
|
||||
}
|
||||
|
||||
static void menu_mod_changed(VariableItem* item) {
|
||||
RollJamApp* app = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
app->mod_index = index;
|
||||
variable_item_set_current_value_text(item, mod_names[index]);
|
||||
|
||||
enforce_min_offset(app, s_offset_item);
|
||||
}
|
||||
|
||||
static void menu_jam_offset_changed(VariableItem* item) {
|
||||
RollJamApp* app = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
uint8_t min_idx = get_min_offset_index(app->mod_index);
|
||||
if(index < min_idx) {
|
||||
index = min_idx;
|
||||
variable_item_set_current_value_index(item, index);
|
||||
}
|
||||
|
||||
app->jam_offset_index = index;
|
||||
app->jam_offset_hz = jam_offset_values[index];
|
||||
variable_item_set_current_value_text(item, jam_offset_names[index]);
|
||||
}
|
||||
|
||||
static void menu_hw_changed(VariableItem* item) {
|
||||
RollJamApp* app = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
app->hw_index = index;
|
||||
variable_item_set_current_value_text(item, hw_names[index]);
|
||||
}
|
||||
|
||||
static void menu_enter_callback(void* context, uint32_t index) {
|
||||
RollJamApp* app = context;
|
||||
if(index == 4) {
|
||||
view_dispatcher_send_custom_event(
|
||||
app->view_dispatcher, RollJamEventStartAttack);
|
||||
}
|
||||
}
|
||||
|
||||
void rolljam_scene_menu_on_enter(void* context) {
|
||||
RollJamApp* app = context;
|
||||
|
||||
variable_item_list_reset(app->var_item_list);
|
||||
|
||||
// --- Frequency ---
|
||||
VariableItem* freq_item = variable_item_list_add(
|
||||
app->var_item_list,
|
||||
"Frequency",
|
||||
FreqIndex_COUNT,
|
||||
menu_freq_changed,
|
||||
app);
|
||||
variable_item_set_current_value_index(freq_item, app->freq_index);
|
||||
variable_item_set_current_value_text(freq_item, freq_names[app->freq_index]);
|
||||
|
||||
// --- Modulation ---
|
||||
VariableItem* mod_item = variable_item_list_add(
|
||||
app->var_item_list,
|
||||
"Modulation",
|
||||
ModIndex_COUNT,
|
||||
menu_mod_changed,
|
||||
app);
|
||||
variable_item_set_current_value_index(mod_item, app->mod_index);
|
||||
variable_item_set_current_value_text(mod_item, mod_names[app->mod_index]);
|
||||
|
||||
// --- Jam Offset ---
|
||||
VariableItem* offset_item = variable_item_list_add(
|
||||
app->var_item_list,
|
||||
"Jam Offset",
|
||||
JamOffIndex_COUNT,
|
||||
menu_jam_offset_changed,
|
||||
app);
|
||||
|
||||
s_offset_item = offset_item;
|
||||
enforce_min_offset(app, offset_item);
|
||||
|
||||
variable_item_set_current_value_index(offset_item, app->jam_offset_index);
|
||||
variable_item_set_current_value_text(offset_item, jam_offset_names[app->jam_offset_index]);
|
||||
|
||||
// --- Hardware ---
|
||||
VariableItem* hw_item = variable_item_list_add(
|
||||
app->var_item_list,
|
||||
"Hardware",
|
||||
HwIndex_COUNT,
|
||||
menu_hw_changed,
|
||||
app);
|
||||
variable_item_set_current_value_index(hw_item, app->hw_index);
|
||||
variable_item_set_current_value_text(hw_item, hw_names[app->hw_index]);
|
||||
|
||||
// --- Start button ---
|
||||
variable_item_list_add(
|
||||
app->var_item_list,
|
||||
">> START ATTACK <<",
|
||||
0,
|
||||
NULL,
|
||||
app);
|
||||
|
||||
variable_item_list_set_enter_callback(
|
||||
app->var_item_list, menu_enter_callback, app);
|
||||
|
||||
view_dispatcher_switch_to_view(
|
||||
app->view_dispatcher, RollJamViewVarItemList);
|
||||
}
|
||||
|
||||
bool rolljam_scene_menu_on_event(void* context, SceneManagerEvent event) {
|
||||
RollJamApp* app = context;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == RollJamEventStartAttack) {
|
||||
enforce_min_offset(app, NULL);
|
||||
|
||||
memset(&app->signal_first, 0, sizeof(RawSignal));
|
||||
memset(&app->signal_second, 0, sizeof(RawSignal));
|
||||
|
||||
scene_manager_next_scene(
|
||||
app->scene_manager, RollJamSceneAttackPhase1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void rolljam_scene_menu_on_exit(void* context) {
|
||||
RollJamApp* app = context;
|
||||
s_offset_item = NULL;
|
||||
variable_item_list_reset(app->var_item_list);
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
#include "rolljam_scene.h"
|
||||
#include "../helpers/rolljam_receiver.h"
|
||||
|
||||
// ============================================================
|
||||
// Phase 4 / Result: user chooses to SAVE or REPLAY 2nd code
|
||||
// ============================================================
|
||||
|
||||
static void result_dialog_callback(DialogExResult result, void* context) {
|
||||
RollJamApp* app = context;
|
||||
|
||||
if(result == DialogExResultLeft) {
|
||||
view_dispatcher_send_custom_event(
|
||||
app->view_dispatcher, RollJamEventSaveSignal);
|
||||
} else if(result == DialogExResultRight) {
|
||||
view_dispatcher_send_custom_event(
|
||||
app->view_dispatcher, RollJamEventReplayNow);
|
||||
}
|
||||
}
|
||||
|
||||
void rolljam_scene_result_on_enter(void* context) {
|
||||
RollJamApp* app = context;
|
||||
|
||||
dialog_ex_reset(app->dialog_ex);
|
||||
|
||||
dialog_ex_set_header(
|
||||
app->dialog_ex, "Attack Complete!",
|
||||
64, 2, AlignCenter, AlignTop);
|
||||
|
||||
dialog_ex_set_text(
|
||||
app->dialog_ex,
|
||||
"1st code: SENT to target\n"
|
||||
"2nd code: IN MEMORY\n\n"
|
||||
"What to do with 2nd?",
|
||||
64, 18, AlignCenter, AlignTop);
|
||||
|
||||
dialog_ex_set_left_button_text(app->dialog_ex, "Save");
|
||||
dialog_ex_set_right_button_text(app->dialog_ex, "Send");
|
||||
|
||||
dialog_ex_set_result_callback(app->dialog_ex, result_dialog_callback);
|
||||
dialog_ex_set_context(app->dialog_ex, app);
|
||||
|
||||
view_dispatcher_switch_to_view(
|
||||
app->view_dispatcher, RollJamViewDialogEx);
|
||||
}
|
||||
|
||||
bool rolljam_scene_result_on_event(void* context, SceneManagerEvent event) {
|
||||
RollJamApp* app = context;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == RollJamEventSaveSignal) {
|
||||
|
||||
rolljam_save_signal(app, &app->signal_second);
|
||||
|
||||
popup_reset(app->popup);
|
||||
popup_set_header(
|
||||
app->popup, "Saved!",
|
||||
64, 20, AlignCenter, AlignCenter);
|
||||
popup_set_text(
|
||||
app->popup,
|
||||
"File saved to:\n/ext/subghz/rolljam_*.sub\n\nPress Back",
|
||||
64, 38, AlignCenter, AlignCenter);
|
||||
popup_set_timeout(app->popup, 5000);
|
||||
popup_enable_timeout(app->popup);
|
||||
view_dispatcher_switch_to_view(
|
||||
app->view_dispatcher, RollJamViewPopup);
|
||||
|
||||
notification_message(app->notification, &sequence_success);
|
||||
return true;
|
||||
|
||||
} else if(event.event == RollJamEventReplayNow) {
|
||||
|
||||
popup_reset(app->popup);
|
||||
popup_set_header(
|
||||
app->popup, "Transmitting...",
|
||||
64, 20, AlignCenter, AlignCenter);
|
||||
popup_set_text(
|
||||
app->popup, "Sending 2nd code NOW",
|
||||
64, 38, AlignCenter, AlignCenter);
|
||||
view_dispatcher_switch_to_view(
|
||||
app->view_dispatcher, RollJamViewPopup);
|
||||
|
||||
rolljam_transmit_signal(app, &app->signal_second);
|
||||
|
||||
notification_message(app->notification, &sequence_success);
|
||||
|
||||
popup_set_header(
|
||||
app->popup, "Done!",
|
||||
64, 20, AlignCenter, AlignCenter);
|
||||
popup_set_text(
|
||||
app->popup,
|
||||
"2nd code transmitted!\n\nPress Back",
|
||||
64, 38, AlignCenter, AlignCenter);
|
||||
popup_set_timeout(app->popup, 5000);
|
||||
popup_enable_timeout(app->popup);
|
||||
|
||||
return true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
scene_manager_search_and_switch_to_another_scene(
|
||||
app->scene_manager, RollJamSceneMenu);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void rolljam_scene_result_on_exit(void* context) {
|
||||
RollJamApp* app = context;
|
||||
dialog_ex_reset(app->dialog_ex);
|
||||
popup_reset(app->popup);
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
#include "rolljam_attack_view.h"
|
||||
#include <gui/canvas.h>
|
||||
|
||||
// ============================================================
|
||||
// Custom drawing for attack status
|
||||
// Reserved for future use with a custom View
|
||||
// Currently the app uses Widget modules instead
|
||||
// ============================================================
|
||||
|
||||
void rolljam_attack_view_draw(Canvas* canvas, AttackViewState* state) {
|
||||
canvas_clear(canvas);
|
||||
|
||||
// Title bar
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 64, 2, AlignCenter, AlignTop, state->phase_text);
|
||||
|
||||
// Separator
|
||||
canvas_draw_line(canvas, 0, 14, 128, 14);
|
||||
|
||||
// Status
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 64, 18, AlignCenter, AlignTop, state->status_text);
|
||||
|
||||
// Indicators
|
||||
int y = 32;
|
||||
|
||||
if(state->jamming) {
|
||||
canvas_draw_str(canvas, 4, y, "JAM: [ACTIVE]");
|
||||
// Animated dots could go here
|
||||
} else {
|
||||
canvas_draw_str(canvas, 4, y, "JAM: [OFF]");
|
||||
}
|
||||
y += 12;
|
||||
|
||||
if(state->capturing) {
|
||||
canvas_draw_str(canvas, 4, y, "RX: [LISTENING]");
|
||||
} else {
|
||||
canvas_draw_str(canvas, 4, y, "RX: [OFF]");
|
||||
}
|
||||
y += 12;
|
||||
|
||||
// Signal counter
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof(buf), "Signals: %d / 2", state->signal_count);
|
||||
canvas_draw_str(canvas, 4, y, buf);
|
||||
|
||||
// Footer
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 64, 62, AlignCenter, AlignBottom, "[BACK] cancel");
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../rolljam.h"
|
||||
|
||||
/*
|
||||
* Custom view for attack visualization.
|
||||
* Currently the app uses Widget and DialogEx for display.
|
||||
* This file is reserved for a future custom canvas-drawn view
|
||||
* (e.g., signal waveform display, animated jamming indicator).
|
||||
*
|
||||
* For now it provides a simple status draw function.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
const char* phase_text;
|
||||
const char* status_text;
|
||||
bool jamming;
|
||||
bool capturing;
|
||||
int signal_count;
|
||||
} AttackViewState;
|
||||
|
||||
// Draw attack status on a canvas (for future custom View use)
|
||||
void rolljam_attack_view_draw(Canvas* canvas, AttackViewState* state);
|
||||
@@ -4,14 +4,12 @@ App(
|
||||
apptype=FlipperAppType.METAPACKAGE,
|
||||
provides=[
|
||||
"gpio",
|
||||
"infrared",
|
||||
"lfrfid",
|
||||
"nfc",
|
||||
"subghz",
|
||||
"rolljam",
|
||||
"subghz_remote",
|
||||
"subghz_bruteforcer",
|
||||
"archive",
|
||||
"subghz_remote",
|
||||
"main_apps_on_start",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -15,6 +15,7 @@ typedef enum {
|
||||
SubGhzCustomEventSceneReceiverInfoTxStart,
|
||||
SubGhzCustomEventSceneReceiverInfoTxStop,
|
||||
SubGhzCustomEventSceneReceiverInfoSave,
|
||||
SubGhzCustomEventSceneReceiverInfoTxFullDpad,
|
||||
SubGhzCustomEventSceneSaveName,
|
||||
SubGhzCustomEventSceneSignalSettings,
|
||||
SubGhzCustomEventSceneSaveSuccess,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#include "subghz_txrx_i.h" // IWYU pragma: keep
|
||||
|
||||
#include <math.h>
|
||||
#include <furi_hal_subghz.h>
|
||||
#include <lib/subghz/protocols/protocol_items.h>
|
||||
#include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h>
|
||||
#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>
|
||||
#include "../../../../lib/subghz/devices/subghz_preset_delta.h"
|
||||
#include <lib/subghz/blocks/custom_btn.h>
|
||||
|
||||
#define TAG "SubGhzTxRx"
|
||||
@@ -359,6 +361,49 @@ SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat*
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool subghz_txrx_rebuild_from_fff(SubGhzTxRx* instance, FlipperFormat* flipper_format) {
|
||||
furi_assert(instance);
|
||||
furi_assert(flipper_format);
|
||||
|
||||
subghz_txrx_stop(instance);
|
||||
|
||||
bool rebuilt = false;
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
SubGhzTransmitter* transmitter = NULL;
|
||||
|
||||
do {
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_read_string(flipper_format, "Protocol", temp_str)) {
|
||||
FURI_LOG_E(TAG, "Missing Protocol");
|
||||
break;
|
||||
}
|
||||
|
||||
transmitter =
|
||||
subghz_transmitter_alloc_init(instance->environment, furi_string_get_cstr(temp_str));
|
||||
if(!transmitter) {
|
||||
FURI_LOG_E(TAG, "Protocol not found");
|
||||
break;
|
||||
}
|
||||
|
||||
rebuilt =
|
||||
subghz_transmitter_deserialize(transmitter, flipper_format) == SubGhzProtocolStatusOk;
|
||||
if(!rebuilt) {
|
||||
FURI_LOG_E(TAG, "Protocol rebuild failed");
|
||||
break;
|
||||
}
|
||||
} while(false);
|
||||
|
||||
if(transmitter) {
|
||||
subghz_transmitter_free(transmitter);
|
||||
}
|
||||
furi_string_free(temp_str);
|
||||
|
||||
return rebuilt;
|
||||
}
|
||||
|
||||
void subghz_txrx_rx_start(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
subghz_txrx_stop(instance);
|
||||
@@ -498,6 +543,36 @@ void subghz_txrx_hopper_pause(SubGhzTxRx* instance) {
|
||||
}
|
||||
}
|
||||
|
||||
// Identify the hop index (0=AM650, 1=FM476, 2=FM95) from the name.
|
||||
// Must match the order defined in subghz_preset_delta.h
|
||||
static int subghz_hop_index_from_name(const char* name) {
|
||||
if(strcmp(name, "AM650") == 0) return 0;
|
||||
if(strcmp(name, "FM476") == 0) return 1;
|
||||
if(strcmp(name, "FM95") == 0) return 2;
|
||||
return -1; // is not part of the fast hopping set
|
||||
}
|
||||
|
||||
// Applies the target preset using delta-patch (without SRES) when possible,
|
||||
// or falls back to the original full reload in any other case.
|
||||
static void subghz_txrx_apply_preset_fast(
|
||||
SubGhzTxRx* instance,
|
||||
const char* old_preset_name,
|
||||
const char* preset_name) {
|
||||
int from_idx = subghz_hop_index_from_name(old_preset_name);
|
||||
int to_idx = subghz_hop_index_from_name(preset_name);
|
||||
|
||||
if(instance->radio_device_type == SubGhzRadioDeviceTypeInternal && from_idx >= 0 &&
|
||||
to_idx >= 0 && from_idx != to_idx) {
|
||||
// Fast path: delta-patch without SRES or full reload (only internal CC1101)
|
||||
const PresetDeltaEntry* e = &preset_delta_table[from_idx][to_idx];
|
||||
furi_hal_subghz_apply_preset_delta(e->delta, e->delta_len, e->needs_scal, e->pa_table);
|
||||
} else {
|
||||
// Fallback: original behavior (full reload)
|
||||
subghz_devices_load_preset(
|
||||
instance->radio_device, FuriHalSubGhzPresetCustom, instance->preset->data);
|
||||
}
|
||||
}
|
||||
|
||||
void subghz_txrx_preset_hopper_update(SubGhzTxRx* instance, float stay_threshold) {
|
||||
furi_assert(instance);
|
||||
|
||||
@@ -550,22 +625,7 @@ void subghz_txrx_preset_hopper_update(SubGhzTxRx* instance, float stay_threshold
|
||||
subghz_txrx_set_preset_internal(
|
||||
instance, instance->preset->frequency, actual_preset_idx, 0);
|
||||
|
||||
bool old_is_am = (strstr(old_preset_name, "AM") != NULL);
|
||||
bool new_is_am = (strstr(preset_name, "AM") != NULL);
|
||||
bool modulation_changed = (old_is_am != new_is_am);
|
||||
|
||||
if(modulation_changed) {
|
||||
subghz_devices_reset(instance->radio_device);
|
||||
subghz_devices_load_preset(
|
||||
instance->radio_device,
|
||||
FuriHalSubGhzPresetCustom,
|
||||
instance->preset->data);
|
||||
} else {
|
||||
subghz_devices_load_preset(
|
||||
instance->radio_device,
|
||||
FuriHalSubGhzPresetCustom,
|
||||
instance->preset->data);
|
||||
}
|
||||
subghz_txrx_apply_preset_fast(instance, old_preset_name, preset_name);
|
||||
|
||||
subghz_txrx_rx(instance, instance->preset->frequency);
|
||||
}
|
||||
@@ -588,22 +648,7 @@ void subghz_txrx_preset_hopper_update(SubGhzTxRx* instance, float stay_threshold
|
||||
subghz_txrx_set_preset_internal(
|
||||
instance, instance->preset->frequency, instance->preset_hopper_idx, 0);
|
||||
|
||||
bool old_is_am = (strstr(old_preset_name, "AM") != NULL);
|
||||
bool new_is_am = (strstr(preset_name, "AM") != NULL);
|
||||
bool modulation_changed = (old_is_am != new_is_am);
|
||||
|
||||
if(modulation_changed) {
|
||||
subghz_devices_reset(instance->radio_device);
|
||||
subghz_devices_load_preset(
|
||||
instance->radio_device,
|
||||
FuriHalSubGhzPresetCustom,
|
||||
instance->preset->data);
|
||||
} else {
|
||||
subghz_devices_load_preset(
|
||||
instance->radio_device,
|
||||
FuriHalSubGhzPresetCustom,
|
||||
instance->preset->data);
|
||||
}
|
||||
subghz_txrx_apply_preset_fast(instance, old_preset_name, preset_name);
|
||||
|
||||
subghz_txrx_rx(instance, instance->preset->frequency);
|
||||
}
|
||||
|
||||
@@ -105,6 +105,15 @@ void subghz_txrx_get_frequency_and_modulation(
|
||||
*/
|
||||
SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Rebuild protocol data without starting TX.
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @param flipper_format Pointer to a FlipperFormat
|
||||
* @return bool True when encoder deserialization updated protocol data successfully
|
||||
*/
|
||||
bool subghz_txrx_rebuild_from_fff(SubGhzTxRx* instance, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Start RX CC1101
|
||||
*
|
||||
|
||||
@@ -34,7 +34,7 @@ typedef struct {
|
||||
bool stop_pending; /* stop requested before MIN_TX_TICKS elapsed */
|
||||
uint32_t tx_start_tick;
|
||||
|
||||
/* Pending button key (InputKey) decoded from the packed custom event */
|
||||
/* Resolved custom button repeated while the physical key is held */
|
||||
uint8_t pending_button;
|
||||
} CarEmulateState;
|
||||
|
||||
@@ -252,12 +252,34 @@ static bool car_emulate_start_tx(SubGhz* subghz, uint8_t custom_btn_id) {
|
||||
|
||||
/** Stop an active transmission. */
|
||||
static void car_emulate_stop_tx(SubGhz* subghz) {
|
||||
subghz_block_generic_global.endless_tx = false;
|
||||
subghz_txrx_stop(subghz->txrx);
|
||||
subghz->state_notifications = SubGhzNotificationStateIDLE;
|
||||
notification_message(subghz->notifications, &sequence_blink_stop);
|
||||
FURI_LOG_I(TAG, "TX stopped");
|
||||
}
|
||||
|
||||
static bool car_emulate_restart_tx(SubGhz* subghz) {
|
||||
furi_assert(s_state);
|
||||
|
||||
car_emulate_update_fff(subghz, s_state->current_counter);
|
||||
subghz_block_generic_global.endless_tx = true;
|
||||
|
||||
if(car_emulate_start_tx(subghz, s_state->pending_button)) {
|
||||
s_state->is_transmitting = true;
|
||||
subghz->state_notifications = SubGhzNotificationStateTx;
|
||||
notification_message(subghz->notifications, &sequence_blink_magenta_10);
|
||||
FURI_LOG_D(TAG, "TX restarted while button is held");
|
||||
return true;
|
||||
}
|
||||
|
||||
subghz_block_generic_global.endless_tx = false;
|
||||
s_state->is_transmitting = false;
|
||||
s_state->stop_pending = false;
|
||||
notification_message(subghz->notifications, &sequence_error);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════════════
|
||||
* View callback (fired from the View's input handler)
|
||||
* ═════════════════════════════════════════════════════════════════════════*/
|
||||
@@ -293,6 +315,7 @@ void subghz_scene_car_emulate_on_enter(void* context) {
|
||||
s_state = malloc(sizeof(CarEmulateState));
|
||||
furi_check(s_state);
|
||||
memset(s_state, 0, sizeof(CarEmulateState));
|
||||
subghz_block_generic_global.endless_tx = false;
|
||||
|
||||
/* ── Read metadata from the loaded fff_data ── */
|
||||
FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx);
|
||||
@@ -401,8 +424,11 @@ bool subghz_scene_car_emulate_on_event(void* context, SceneManagerEvent event) {
|
||||
s_state->tx_start_tick = (uint32_t)furi_get_tick();
|
||||
|
||||
uint8_t cur_btn = subghz_custom_btn_get();
|
||||
s_state->pending_button = cur_btn;
|
||||
subghz_block_generic_global.endless_tx = true;
|
||||
if(!car_emulate_start_tx(subghz, cur_btn)) {
|
||||
s_state->is_transmitting = false;
|
||||
subghz_block_generic_global.endless_tx = false;
|
||||
notification_message(subghz->notifications, &sequence_error);
|
||||
}
|
||||
|
||||
@@ -411,11 +437,13 @@ bool subghz_scene_car_emulate_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
/* ── Stop ── */
|
||||
} else if(event.event == SubGhzCustomEventCarEmulateStop) {
|
||||
if(s_state->is_transmitting &&
|
||||
subghz->state_notifications == SubGhzNotificationStateTx) {
|
||||
if(s_state->is_transmitting) {
|
||||
subghz_block_generic_global.endless_tx = false;
|
||||
|
||||
uint32_t elapsed = (uint32_t)furi_get_tick() - s_state->tx_start_tick;
|
||||
if(elapsed >= MIN_TX_TICKS) {
|
||||
if(
|
||||
elapsed >= MIN_TX_TICKS &&
|
||||
subghz->state_notifications == SubGhzNotificationStateTx) {
|
||||
car_emulate_stop_tx(subghz);
|
||||
s_state->is_transmitting = false;
|
||||
s_state->stop_pending = false;
|
||||
@@ -431,6 +459,7 @@ bool subghz_scene_car_emulate_on_event(void* context, SceneManagerEvent event) {
|
||||
if(subghz->state_notifications == SubGhzNotificationStateTx) {
|
||||
car_emulate_stop_tx(subghz);
|
||||
}
|
||||
subghz_block_generic_global.endless_tx = false;
|
||||
scene_manager_search_and_switch_to_previous_scene(
|
||||
subghz->scene_manager, SubGhzSceneSavedMenu);
|
||||
consumed = true;
|
||||
@@ -450,6 +479,8 @@ bool subghz_scene_car_emulate_on_event(void* context, SceneManagerEvent event) {
|
||||
s_state->is_transmitting = false;
|
||||
s_state->stop_pending = false;
|
||||
notification_message(subghz->notifications, &sequence_blink_stop);
|
||||
} else {
|
||||
car_emulate_restart_tx(subghz);
|
||||
}
|
||||
} else {
|
||||
/* Still transmitting – blink LED */
|
||||
@@ -485,6 +516,7 @@ void subghz_scene_car_emulate_on_exit(void* context) {
|
||||
car_emulate_stop_tx(subghz);
|
||||
}
|
||||
|
||||
subghz_block_generic_global.endless_tx = false;
|
||||
subghz->state_notifications = SubGhzNotificationStateIDLE;
|
||||
notification_message(subghz->notifications, &sequence_blink_stop);
|
||||
|
||||
|
||||
@@ -187,12 +187,15 @@ bool subghz_scene_keeloq_decrypt_on_event(void* context, SceneManagerEvent event
|
||||
if(!subghz->keeloq_keys_manager) {
|
||||
subghz->keeloq_keys_manager = subghz_keeloq_keys_alloc();
|
||||
}
|
||||
uint16_t learning_type = ctx->recovered_type ?
|
||||
ctx->recovered_type :
|
||||
KEELOQ_LEARNING_SIMPLE;
|
||||
char key_name[24];
|
||||
snprintf(key_name, sizeof(key_name), "BF_%07lX", ctx->serial);
|
||||
subghz_keeloq_keys_add(
|
||||
subghz->keeloq_keys_manager,
|
||||
ctx->recovered_mfkey,
|
||||
KEELOQ_LEARNING_SIMPLE,
|
||||
learning_type,
|
||||
key_name);
|
||||
subghz_keeloq_keys_save(subghz->keeloq_keys_manager);
|
||||
|
||||
@@ -202,7 +205,7 @@ bool subghz_scene_keeloq_decrypt_on_event(void* context, SceneManagerEvent event
|
||||
SubGhzKey* entry = SubGhzKeyArray_push_raw(*env_arr);
|
||||
entry->name = furi_string_alloc_set(key_name);
|
||||
entry->key = ctx->recovered_mfkey;
|
||||
entry->type = KEELOQ_LEARNING_SIMPLE;
|
||||
entry->type = learning_type;
|
||||
return true;
|
||||
|
||||
} else if(event.event == KL_DECRYPT_EVENT_DONE) {
|
||||
|
||||
@@ -1,105 +1,68 @@
|
||||
#include "../subghz_i.h"
|
||||
#include <lib/subghz/subghz_protocol_registry.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define TAG "SubGhzSceneProtocolList"
|
||||
|
||||
/* ── helpers ──────────────────────────────────────────────────────────────── */
|
||||
typedef struct {
|
||||
SubGhz* subghz;
|
||||
const char* protocol_name;
|
||||
} SubGhzProtocolListItemContext;
|
||||
|
||||
static bool proto_filter_contains(const char* filter, const char* name) {
|
||||
const char* p = filter;
|
||||
while(*p) {
|
||||
const char* comma = strchr(p, ',');
|
||||
size_t len = comma ? (size_t)(comma - p) : strlen(p);
|
||||
if(len == strlen(name) && strncmp(p, name, len) == 0) return true;
|
||||
if(!comma) break;
|
||||
p = comma + 1;
|
||||
}
|
||||
return false;
|
||||
static SubGhzProtocolListItemContext* protocol_list_item_contexts = NULL;
|
||||
|
||||
static void subghz_scene_protocol_list_free_contexts(void) {
|
||||
free(protocol_list_item_contexts);
|
||||
protocol_list_item_contexts = NULL;
|
||||
}
|
||||
|
||||
static void proto_filter_toggle(char* filter, size_t filter_size, const char* name) {
|
||||
if(proto_filter_contains(filter, name)) {
|
||||
/* remove it */
|
||||
char tmp[256] = {0};
|
||||
const char* p = filter;
|
||||
bool first = true;
|
||||
while(*p) {
|
||||
const char* comma = strchr(p, ',');
|
||||
size_t len = comma ? (size_t)(comma - p) : strlen(p);
|
||||
if(!(len == strlen(name) && strncmp(p, name, len) == 0)) {
|
||||
if(!first) strncat(tmp, ",", sizeof(tmp) - strlen(tmp) - 1);
|
||||
strncat(tmp, p, len < sizeof(tmp) - strlen(tmp) - 1 ? len : 0);
|
||||
first = false;
|
||||
}
|
||||
if(!comma) break;
|
||||
p = comma + 1;
|
||||
}
|
||||
strncpy(filter, tmp, filter_size - 1);
|
||||
filter[filter_size - 1] = '\0';
|
||||
} else {
|
||||
/* add it */
|
||||
if(filter[0] != '\0') strncat(filter, ",", filter_size - strlen(filter) - 1);
|
||||
strncat(filter, name, filter_size - strlen(filter) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* ── callbacks ────────────────────────────────────────────────────────────── */
|
||||
|
||||
static void subghz_scene_protocol_list_item_changed(VariableItem* item) {
|
||||
SubGhz* subghz = variable_item_get_context(item);
|
||||
uint8_t value_index = variable_item_get_current_value_index(item);
|
||||
SubGhzProtocolListItemContext* item_context = variable_item_get_context(item);
|
||||
if(!item_context || !item_context->subghz || !item_context->protocol_name) return;
|
||||
|
||||
uint32_t proto_idx =
|
||||
scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneProtocolList);
|
||||
size_t selected =
|
||||
variable_item_list_get_selected_item_index(subghz->variable_item_list);
|
||||
UNUSED(proto_idx);
|
||||
SubGhz* subghz = item_context->subghz;
|
||||
bool should_disable = variable_item_get_current_value_index(item) == 1;
|
||||
|
||||
const SubGhzProtocol* protocol =
|
||||
subghz_protocol_registry_get_by_index(&subghz_protocol_registry, selected);
|
||||
if(!protocol) return;
|
||||
bool changed = subghz_last_settings_protocol_filter_set(
|
||||
subghz->last_settings, item_context->protocol_name, should_disable);
|
||||
bool is_disabled = subghz_last_settings_protocol_filter_contains(
|
||||
subghz->last_settings, item_context->protocol_name);
|
||||
|
||||
const char* name = protocol->name;
|
||||
|
||||
bool currently_in =
|
||||
proto_filter_contains(subghz->last_settings->protocol_filter, name);
|
||||
|
||||
if((value_index == 1) != currently_in) {
|
||||
proto_filter_toggle(
|
||||
subghz->last_settings->protocol_filter,
|
||||
sizeof(subghz->last_settings->protocol_filter),
|
||||
name);
|
||||
}
|
||||
|
||||
variable_item_set_current_value_text(item, value_index ? "ONLY" : "---");
|
||||
subghz_last_settings_save(subghz->last_settings);
|
||||
variable_item_set_current_value_index(item, is_disabled ? 1 : 0);
|
||||
variable_item_set_current_value_text(item, is_disabled ? "OFF" : "ON");
|
||||
if(changed) subghz_last_settings_save(subghz->last_settings);
|
||||
}
|
||||
|
||||
/* ── scene callbacks ──────────────────────────────────────────────────────── */
|
||||
|
||||
void subghz_scene_protocol_list_on_enter(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
VariableItemList* list = subghz->variable_item_list;
|
||||
variable_item_list_reset(list);
|
||||
subghz_scene_protocol_list_free_contexts();
|
||||
|
||||
size_t protocol_count = subghz_protocol_registry_count(&subghz_protocol_registry);
|
||||
protocol_list_item_contexts =
|
||||
malloc(sizeof(SubGhzProtocolListItemContext) * protocol_count);
|
||||
furi_check(protocol_list_item_contexts);
|
||||
|
||||
for(size_t i = 0; i < protocol_count; i++) {
|
||||
const SubGhzProtocol* protocol =
|
||||
subghz_protocol_registry_get_by_index(&subghz_protocol_registry, i);
|
||||
if(!protocol) continue;
|
||||
|
||||
protocol_list_item_contexts[i].subghz = subghz;
|
||||
protocol_list_item_contexts[i].protocol_name = protocol->name;
|
||||
|
||||
VariableItem* item = variable_item_list_add(
|
||||
list,
|
||||
protocol->name,
|
||||
2,
|
||||
subghz_scene_protocol_list_item_changed,
|
||||
subghz);
|
||||
&protocol_list_item_contexts[i]);
|
||||
|
||||
bool enabled = proto_filter_contains(
|
||||
subghz->last_settings->protocol_filter, protocol->name);
|
||||
variable_item_set_current_value_index(item, enabled ? 1 : 0);
|
||||
variable_item_set_current_value_text(item, enabled ? "ONLY" : "---");
|
||||
bool is_disabled =
|
||||
subghz_last_settings_protocol_filter_contains(subghz->last_settings, protocol->name);
|
||||
variable_item_set_current_value_index(item, is_disabled ? 1 : 0);
|
||||
variable_item_set_current_value_text(item, is_disabled ? "OFF" : "ON");
|
||||
}
|
||||
|
||||
variable_item_list_set_selected_item(
|
||||
@@ -132,4 +95,5 @@ void subghz_scene_protocol_list_on_exit(void* context) {
|
||||
SubGhzSceneProtocolList,
|
||||
variable_item_list_get_selected_item_index(subghz->variable_item_list));
|
||||
variable_item_list_reset(subghz->variable_item_list);
|
||||
subghz_scene_protocol_list_free_contexts();
|
||||
}
|
||||
|
||||
@@ -105,27 +105,10 @@ static void subghz_scene_add_to_history_callback(
|
||||
SubGhz* subghz = context;
|
||||
|
||||
// The check can be moved to /lib/subghz/receiver.c, but may result in false positives
|
||||
/* Protocol name allowlist filter — if non-empty, drop anything not in the list */
|
||||
if(subghz->last_settings->protocol_filter[0] != '\0') {
|
||||
const char* proto_name = decoder_base->protocol->name;
|
||||
const char* filter = subghz->last_settings->protocol_filter;
|
||||
bool allowed = false;
|
||||
/* Walk the comma-separated list */
|
||||
const char* p = filter;
|
||||
while(*p) {
|
||||
const char* comma = strchr(p, ',');
|
||||
size_t len = comma ? (size_t)(comma - p) : strlen(p);
|
||||
if(len == strlen(proto_name) && strncmp(p, proto_name, len) == 0) {
|
||||
allowed = true;
|
||||
break;
|
||||
}
|
||||
if(!comma) break;
|
||||
p = comma + 1;
|
||||
}
|
||||
if(!allowed) {
|
||||
FURI_LOG_D(TAG, "%s filtered by allowlist", proto_name);
|
||||
return;
|
||||
}
|
||||
if(subghz_last_settings_protocol_filter_contains(
|
||||
subghz->last_settings, decoder_base->protocol->name)) {
|
||||
FURI_LOG_D(TAG, "%s filtered by protocol OFF list", decoder_base->protocol->name);
|
||||
return;
|
||||
}
|
||||
|
||||
if((decoder_base->protocol->flag & subghz->ignore_filter) == 0) {
|
||||
|
||||
@@ -679,15 +679,13 @@ void subghz_scene_receiver_config_on_enter(void* context) {
|
||||
1,
|
||||
NULL,
|
||||
subghz);
|
||||
if(subghz->last_settings->protocol_filter[0] == '\0') {
|
||||
variable_item_set_current_value_text(item, "All");
|
||||
uint8_t protocol_filter_count =
|
||||
subghz_last_settings_protocol_filter_count(subghz->last_settings);
|
||||
if(protocol_filter_count == 0) {
|
||||
variable_item_set_current_value_text(item, "All ON");
|
||||
} else {
|
||||
uint8_t count = 1;
|
||||
for(const char* p = subghz->last_settings->protocol_filter; *p; p++) {
|
||||
if(*p == ',') count++;
|
||||
}
|
||||
static char filter_count_str[8];
|
||||
snprintf(filter_count_str, sizeof(filter_count_str), "%u set", count);
|
||||
snprintf(filter_count_str, sizeof(filter_count_str), "%u OFF", protocol_filter_count);
|
||||
variable_item_set_current_value_text(item, filter_count_str);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "../subghz_i.h"
|
||||
|
||||
#include <lib/subghz/blocks/custom_btn.h>
|
||||
#include <flipper_format/flipper_format_i.h>
|
||||
|
||||
#include "applications/main/subghz/helpers/subghz_txrx_i.h"
|
||||
#include <lib/subghz/blocks/generic.h>
|
||||
@@ -20,6 +21,9 @@ void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, v
|
||||
} else if((result == GuiButtonTypeRight) && (type == InputTypeShort)) {
|
||||
view_dispatcher_send_custom_event(
|
||||
subghz->view_dispatcher, SubGhzCustomEventSceneReceiverInfoSave);
|
||||
} else if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) {
|
||||
view_dispatcher_send_custom_event(
|
||||
subghz->view_dispatcher, SubGhzCustomEventSceneReceiverInfoTxFullDpad);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +33,6 @@ static bool subghz_scene_receiver_info_update_parser(void* context) {
|
||||
if(subghz_txrx_load_decoder_by_name_protocol(
|
||||
subghz->txrx,
|
||||
subghz_history_get_protocol_name(subghz->history, subghz->idx_menu_chosen))) {
|
||||
// we are trying to deserialize without checking for errors, since it is assumed that we just received this chignal
|
||||
subghz_protocol_decoder_base_deserialize(
|
||||
subghz_txrx_get_decoder(subghz->txrx),
|
||||
subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen));
|
||||
@@ -37,7 +40,6 @@ static bool subghz_scene_receiver_info_update_parser(void* context) {
|
||||
SubGhzRadioPreset* preset =
|
||||
subghz_history_get_radio_preset(subghz->history, subghz->idx_menu_chosen);
|
||||
|
||||
//Edit TX power, if necessary.
|
||||
subghz_txrx_set_tx_power(preset->data, preset->data_size, subghz->tx_power);
|
||||
|
||||
subghz_txrx_set_preset(
|
||||
@@ -93,7 +95,7 @@ void subghz_scene_receiver_info_draw_widget(SubGhz* subghz) {
|
||||
subghz_scene_receiver_info_callback,
|
||||
subghz);
|
||||
}
|
||||
// Removed static check
|
||||
|
||||
if(subghz_txrx_protocol_is_transmittable(subghz->txrx, false)) {
|
||||
widget_add_button_element(
|
||||
subghz->widget,
|
||||
@@ -101,9 +103,14 @@ void subghz_scene_receiver_info_draw_widget(SubGhz* subghz) {
|
||||
"Send",
|
||||
subghz_scene_receiver_info_callback,
|
||||
subghz);
|
||||
widget_add_button_element(
|
||||
subghz->widget,
|
||||
GuiButtonTypeLeft,
|
||||
"Full",
|
||||
subghz_scene_receiver_info_callback,
|
||||
subghz);
|
||||
}
|
||||
} else {
|
||||
// [NO_DOLPHIN] widget_add_icon_element(subghz->widget, 83, 22, &I_WarningDolphinFlip_45x42);
|
||||
widget_add_string_element(
|
||||
subghz->widget, 13, 8, AlignLeft, AlignBottom, FontSecondary, "Error history parse.");
|
||||
}
|
||||
@@ -131,12 +138,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
|
||||
if(!subghz_scene_receiver_info_update_parser(subghz)) {
|
||||
return false;
|
||||
}
|
||||
//CC1101 Stop RX -> Start TX
|
||||
subghz_txrx_hopper_pause(subghz->txrx);
|
||||
// key concept: we start endless TX until user release OK button, and after this we send last
|
||||
// protocols repeats - this guarantee that one press OK will
|
||||
// be guarantee send the required minimum protocol data packets
|
||||
// for all of this we use subghz_block_generic_global.endless_tx in protocols _yield function.
|
||||
subghz->state_notifications = SubGhzNotificationStateTx;
|
||||
subghz_block_generic_global.endless_tx = true;
|
||||
if(!subghz_tx_start(
|
||||
@@ -146,37 +148,51 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
|
||||
subghz_txrx_hopper_unpause(subghz->txrx);
|
||||
subghz->state_notifications = SubGhzNotificationStateRx;
|
||||
subghz_block_generic_global.endless_tx = false;
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
|
||||
} else if(event.event == SubGhzCustomEventSceneReceiverInfoTxStop) {
|
||||
//CC1101 Stop Tx -> next tick event Start RX
|
||||
// user release OK
|
||||
// we switch off endless_tx - that mean protocols yield finish endless transmission,
|
||||
// send upload "repeat=xx" times, and after will be stoped by the tick event down in this code
|
||||
subghz->state_notifications = SubGhzNotificationStateTxWait;
|
||||
subghz_block_generic_global.endless_tx = false;
|
||||
|
||||
return true;
|
||||
|
||||
} else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) {
|
||||
//CC1101 Stop RX -> Save
|
||||
subghz->state_notifications = SubGhzNotificationStateIDLE;
|
||||
subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF);
|
||||
|
||||
subghz_txrx_stop(subghz->txrx);
|
||||
if(!subghz_scene_receiver_info_update_parser(subghz)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(subghz_txrx_protocol_is_serializable(subghz->txrx)) {
|
||||
subghz_file_name_clear(subghz);
|
||||
|
||||
subghz->save_datetime =
|
||||
subghz_history_get_datetime(subghz->history, subghz->idx_menu_chosen);
|
||||
subghz->save_datetime_set = true;
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);
|
||||
}
|
||||
return true;
|
||||
|
||||
} else if(event.event == SubGhzCustomEventSceneReceiverInfoTxFullDpad) {
|
||||
if(!subghz_scene_receiver_info_update_parser(subghz)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FlipperFormat* fff_history =
|
||||
subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen);
|
||||
FlipperFormat* fff_data = subghz_txrx_get_fff_data(subghz->txrx);
|
||||
|
||||
Stream* src = flipper_format_get_raw_stream(fff_history);
|
||||
Stream* dst = flipper_format_get_raw_stream(fff_data);
|
||||
|
||||
stream_seek(src, 0, StreamOffsetFromStart);
|
||||
stream_clean(dst);
|
||||
stream_copy_full(src, dst);
|
||||
stream_seek(dst, 0, StreamOffsetFromStart);
|
||||
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter);
|
||||
return true;
|
||||
}
|
||||
|
||||
} else if(event.type == SceneManagerEventTypeTick) {
|
||||
if(subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF) {
|
||||
subghz_txrx_hopper_update(subghz->txrx, subghz->last_settings->hopping_threshold);
|
||||
@@ -193,18 +209,15 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
|
||||
subghz->state_notifications = SubGhzNotificationStateRx;
|
||||
break;
|
||||
case SubGhzNotificationStateTxWait:
|
||||
// we wait until hardware TX finished and after stop TX and start RX, else just blink led
|
||||
if(!subghz_devices_is_async_complete_tx(subghz->txrx->radio_device)) {
|
||||
notification_message(subghz->notifications, &sequence_blink_magenta_10);
|
||||
} else {
|
||||
subghz_txrx_stop(subghz->txrx);
|
||||
// update screen
|
||||
widget_reset(subghz->widget);
|
||||
subghz_scene_receiver_info_draw_widget(subghz);
|
||||
|
||||
subghz->state_notifications = SubGhzNotificationStateIDLE;
|
||||
|
||||
if(!scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneDecodeRAW)) {
|
||||
if(!scene_manager_has_previous_scene(
|
||||
subghz->scene_manager, SubGhzSceneDecodeRAW)) {
|
||||
subghz_txrx_rx_start(subghz->txrx);
|
||||
subghz_txrx_hopper_unpause(subghz->txrx);
|
||||
if(!subghz_history_get_text_space_left(subghz->history, NULL)) {
|
||||
@@ -222,7 +235,6 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
|
||||
|
||||
void subghz_scene_receiver_info_on_exit(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
|
||||
widget_reset(subghz->widget);
|
||||
subghz_txrx_reset_dynamic_and_custom_btns(subghz->txrx);
|
||||
}
|
||||
|
||||
@@ -110,6 +110,7 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {
|
||||
} else if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubGhzCustomEventSceneSaveName) {
|
||||
if(strcmp(subghz->file_name_tmp, "") != 0) {
|
||||
furi_string_reset(subghz->error_str);
|
||||
furi_string_cat_printf(
|
||||
subghz->file_path,
|
||||
"/%s%s",
|
||||
|
||||
@@ -12,8 +12,14 @@ void subghz_scene_save_success_on_enter(void* context) {
|
||||
// Setup view
|
||||
Popup* popup = subghz->popup;
|
||||
// [NO_DOLPHIN] popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58);
|
||||
popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom);
|
||||
popup_set_timeout(popup, 1500);
|
||||
if(furi_string_size(subghz->error_str)) {
|
||||
popup_set_header(popup, "Saved", 15, 4, AlignLeft, AlignTop);
|
||||
popup_set_text(popup, furi_string_get_cstr(subghz->error_str), 4, 18, AlignLeft, AlignTop);
|
||||
popup_set_timeout(popup, 2500);
|
||||
} else {
|
||||
popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom);
|
||||
popup_set_timeout(popup, 1500);
|
||||
}
|
||||
popup_set_context(popup, subghz);
|
||||
popup_set_callback(popup, subghz_scene_save_success_popup_callback);
|
||||
popup_enable_timeout(popup);
|
||||
@@ -72,4 +78,5 @@ void subghz_scene_save_success_on_exit(void* context) {
|
||||
Popup* popup = subghz->popup;
|
||||
|
||||
popup_reset(popup);
|
||||
furi_string_reset(subghz->error_str);
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
enum SubmenuIndex {
|
||||
SubmenuIndexEmulate,
|
||||
SubmenuIndexSignalSettings,
|
||||
SubmenuIndexPsaDecrypt,
|
||||
SubmenuIndexEdit,
|
||||
SubmenuIndexDelete,
|
||||
SubmenuIndexSignalSettings,
|
||||
SubmenuIndexCounterBf, /* <-- comma was missing here */
|
||||
SubmenuIndexCarEmulateSettings,
|
||||
};
|
||||
@@ -20,17 +20,20 @@ void subghz_scene_saved_menu_on_enter(void* context) {
|
||||
|
||||
FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx);
|
||||
bool is_psa_encrypted = false;
|
||||
bool has_signal_editor = false;
|
||||
bool has_counter = false;
|
||||
if(fff) {
|
||||
FuriString* proto = furi_string_alloc();
|
||||
flipper_format_rewind(fff);
|
||||
if(flipper_format_read_string(fff, "Protocol", proto)) {
|
||||
has_signal_editor = !furi_string_equal_str(proto, "RAW");
|
||||
if(furi_string_equal_str(proto, "PSA GROUP")) {
|
||||
FuriString* type_str = furi_string_alloc();
|
||||
flipper_format_rewind(fff);
|
||||
if(!flipper_format_read_string(fff, "Type", type_str) ||
|
||||
furi_string_equal_str(type_str, "00")) {
|
||||
is_psa_encrypted = true;
|
||||
has_signal_editor = false;
|
||||
}
|
||||
furi_string_free(type_str);
|
||||
}
|
||||
@@ -53,6 +56,15 @@ void subghz_scene_saved_menu_on_enter(void* context) {
|
||||
SubmenuIndexEmulate,
|
||||
subghz_scene_saved_menu_submenu_callback,
|
||||
subghz);
|
||||
|
||||
if(has_signal_editor) {
|
||||
submenu_add_item(
|
||||
subghz->submenu,
|
||||
"Signal Editor",
|
||||
SubmenuIndexSignalSettings,
|
||||
subghz_scene_saved_menu_submenu_callback,
|
||||
subghz);
|
||||
}
|
||||
}
|
||||
|
||||
if(is_psa_encrypted) {
|
||||
@@ -85,15 +97,6 @@ void subghz_scene_saved_menu_on_enter(void* context) {
|
||||
subghz_scene_saved_menu_submenu_callback,
|
||||
subghz);
|
||||
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
submenu_add_item(
|
||||
subghz->submenu,
|
||||
"Signal Settings",
|
||||
SubmenuIndexSignalSettings,
|
||||
subghz_scene_saved_menu_submenu_callback,
|
||||
subghz);
|
||||
}
|
||||
|
||||
if(has_counter) {
|
||||
submenu_add_item(
|
||||
subghz->submenu,
|
||||
@@ -118,17 +121,7 @@ bool subghz_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
|
||||
scene_manager_set_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneSavedMenu, SubmenuIndexEmulate);
|
||||
|
||||
bool use_custom = subghz->last_settings->custom_car_emulate;
|
||||
if(use_custom) {
|
||||
FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx);
|
||||
uint32_t cnt_tmp = 0;
|
||||
flipper_format_rewind(fff);
|
||||
if(!flipper_format_read_uint32(fff, "Cnt", &cnt_tmp, 1)) {
|
||||
use_custom = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(use_custom) {
|
||||
if(subghz->last_settings->custom_car_emulate) {
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneCarEmulate);
|
||||
} else {
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter);
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
#include <machine/endian.h>
|
||||
#include <toolbox/strint.h>
|
||||
#include <lib/subghz/blocks/generic.h>
|
||||
#include <lib/subghz/blocks/custom_btn_i.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define TAG "SubGhzSceneSignalSettings"
|
||||
|
||||
@@ -13,6 +16,8 @@ static uint32_t counter32 = 0x0;
|
||||
static uint16_t counter16 = 0x0;
|
||||
static uint8_t cnt_byte_count = 0;
|
||||
static uint8_t* cnt_byte_ptr = NULL;
|
||||
static uint32_t counter_value = 0;
|
||||
static uint32_t counter_mask = 0xffffffffUL;
|
||||
|
||||
static FuriString* byte_input_text;
|
||||
|
||||
@@ -21,6 +26,26 @@ static uint8_t btn_byte_count = 1;
|
||||
static uint8_t* btn_byte_ptr = NULL;
|
||||
|
||||
static uint8_t submenu_called = 0;
|
||||
static bool button_uses_custom_btn = false;
|
||||
static uint8_t button_custom_id = SUBGHZ_CUSTOM_BTN_OK;
|
||||
|
||||
static bool subghz_scene_signal_settings_rebuild_save_reload(
|
||||
SubGhz* subghz,
|
||||
bool use_custom_btn,
|
||||
uint8_t custom_btn_id);
|
||||
|
||||
enum {
|
||||
SignalSettingsIndexCounterMode,
|
||||
SignalSettingsIndexCounter,
|
||||
SignalSettingsIndexButton,
|
||||
};
|
||||
|
||||
enum {
|
||||
SignalSettingsCounterStepDown,
|
||||
SignalSettingsCounterStepValue,
|
||||
SignalSettingsCounterStepUp,
|
||||
SignalSettingsCounterStepCount,
|
||||
};
|
||||
|
||||
#define COUNTER_MODE_COUNT 8
|
||||
static const char* const counter_mode_text[COUNTER_MODE_COUNT] = {
|
||||
@@ -45,6 +70,65 @@ static const int32_t counter_mode_value[COUNTER_MODE_COUNT] = {
|
||||
7,
|
||||
};
|
||||
|
||||
static const uint8_t button_value[] = {
|
||||
SUBGHZ_CUSTOM_BTN_OK,
|
||||
SUBGHZ_CUSTOM_BTN_UP,
|
||||
SUBGHZ_CUSTOM_BTN_DOWN,
|
||||
SUBGHZ_CUSTOM_BTN_LEFT,
|
||||
SUBGHZ_CUSTOM_BTN_RIGHT,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
};
|
||||
|
||||
#define BUTTON_VALUE_COUNT (sizeof(button_value) / sizeof(button_value[0]))
|
||||
|
||||
static const char* const button_default_labels[BUTTON_VALUE_COUNT] = {
|
||||
"Original",
|
||||
"Up",
|
||||
"Down",
|
||||
"Left",
|
||||
"Right",
|
||||
"Button 5",
|
||||
"Button 6",
|
||||
"Button 7",
|
||||
};
|
||||
|
||||
static const char* button_labels[BUTTON_VALUE_COUNT] = {
|
||||
"Original",
|
||||
"Up",
|
||||
"Down",
|
||||
"Left",
|
||||
"Right",
|
||||
"Button 5",
|
||||
"Button 6",
|
||||
"Button 7",
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
const char* protocol;
|
||||
const char* labels[BUTTON_VALUE_COUNT];
|
||||
} ProtocolButtonLabels;
|
||||
|
||||
static const ProtocolButtonLabels protocol_button_labels[] = {
|
||||
{"VAG GROUP", {"Original", "Lock", "Unlock", "Trunk", "Panic"}},
|
||||
{"Porsche AG", {"Original", "Lock", "Unlock", "Trunk", "Open"}},
|
||||
{"FORD V0", {"Original", "Lock", "Unlock", "Trunk"}},
|
||||
{"Ford V2", {"Unlock", "Lock", "Trunk", "Panic", "Remote Start"}},
|
||||
{"PSA GROUP", {"Original", "Lock", "Unlock", "Trunk", "Trunk"}},
|
||||
{"KIA/HYU V0", {"Original", "Lock", "Unlock", "Trunk", "Horn"}},
|
||||
{"KIA/HYU V1", {"Original", "Lock", "Unlock", "Trunk", "Panic"}},
|
||||
{"KIA/HYU V2", {"Original", "Lock", "Unlock", "Trunk", "Panic"}},
|
||||
{"KIA/HYU V3/V4", {"Original", "Lock", "Unlock", "Trunk", "Panic", "Horn"}},
|
||||
{"KIA/HYU V5", {"Original", "Unlock", "Lock", "Trunk", "Horn"}},
|
||||
{"KIA/HYU V6", {"Original", "Lock", "Unlock", "Trunk", "Panic"}},
|
||||
{"SUBARU", {"Original", "Lock", "Unlock", "Trunk", "Panic", "Extra"}},
|
||||
{"SUZUKI", {"Original", "Lock", "Unlock", "Trunk", "Panic"}},
|
||||
{"Star Line", {"Original", "Lock", "Unlock", "Trunk", "Start"}},
|
||||
{"Scher-Khan", {"Original", "Lock", "Unlock", "Trunk", "Start"}},
|
||||
{"Sheriff CFM", {"Original", "Lock", "Unlock", "Trunk", "Panic"}},
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
char* name;
|
||||
uint8_t mode_count;
|
||||
@@ -59,7 +143,225 @@ static Protocols protocols[] = {
|
||||
{"Phoenix_V2", 3},
|
||||
};
|
||||
|
||||
#define PROTOCOLS_COUNT (sizeof(protocols) / sizeof(Protocols));
|
||||
#define PROTOCOLS_COUNT (sizeof(protocols) / sizeof(Protocols))
|
||||
|
||||
static void subghz_scene_signal_settings_reset_button_labels(void) {
|
||||
for(uint8_t i = 0; i < BUTTON_VALUE_COUNT; i++) {
|
||||
button_labels[i] = button_default_labels[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void subghz_scene_signal_settings_apply_button_labels(const char* protocol) {
|
||||
for(uint8_t i = 0; i < COUNT_OF(protocol_button_labels); i++) {
|
||||
if(strcmp(protocol, protocol_button_labels[i].protocol) == 0) {
|
||||
for(uint8_t btn = 0; btn < BUTTON_VALUE_COUNT; btn++) {
|
||||
if(protocol_button_labels[i].labels[btn]) {
|
||||
button_labels[btn] = protocol_button_labels[i].labels[btn];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const char* subghz_scene_signal_settings_get_button_label(uint8_t custom_btn_id) {
|
||||
if(custom_btn_id == SUBGHZ_CUSTOM_BTN_OK) {
|
||||
uint8_t original = subghz_custom_btn_get_original();
|
||||
if((original != SUBGHZ_CUSTOM_BTN_OK) && (original < BUTTON_VALUE_COUNT)) {
|
||||
return button_labels[original];
|
||||
}
|
||||
}
|
||||
if(custom_btn_id < BUTTON_VALUE_COUNT) {
|
||||
return button_labels[custom_btn_id];
|
||||
}
|
||||
return "Button";
|
||||
}
|
||||
|
||||
static bool subghz_scene_signal_settings_update_uint32_field(
|
||||
FlipperFormat* fff,
|
||||
const char* key,
|
||||
uint32_t value) {
|
||||
flipper_format_rewind(fff);
|
||||
return flipper_format_insert_or_update_uint32(fff, key, &value, 1);
|
||||
}
|
||||
|
||||
static uint32_t subghz_scene_signal_settings_counter_get_mask(void) {
|
||||
uint8_t bits = subghz_block_generic_global.cnt_length_bit;
|
||||
if((bits == 0) || (bits >= 32)) {
|
||||
return 0xffffffffUL;
|
||||
}
|
||||
return (1UL << bits) - 1UL;
|
||||
}
|
||||
|
||||
static void subghz_scene_signal_settings_counter_sync_byte_input(void) {
|
||||
if(cnt_byte_count == 4) {
|
||||
counter32 = __bswap32(counter_value);
|
||||
cnt_byte_ptr = (uint8_t*)&counter32;
|
||||
} else if(cnt_byte_count == 2) {
|
||||
counter16 = __bswap16((uint16_t)counter_value);
|
||||
cnt_byte_ptr = (uint8_t*)&counter16;
|
||||
}
|
||||
}
|
||||
|
||||
static void subghz_scene_signal_settings_counter_set_value(uint32_t value) {
|
||||
counter_value = value & counter_mask;
|
||||
subghz_scene_signal_settings_counter_sync_byte_input();
|
||||
}
|
||||
|
||||
static void subghz_scene_signal_settings_counter_format(FuriString* text) {
|
||||
if(cnt_byte_count == 4) {
|
||||
furi_string_printf(text, "%lX", (unsigned long)counter_value);
|
||||
} else {
|
||||
furi_string_printf(text, "%X", (unsigned int)(counter_value & 0xffffU));
|
||||
}
|
||||
}
|
||||
|
||||
static void subghz_scene_signal_settings_counter_update_item(VariableItem* item) {
|
||||
char text[9] = {0};
|
||||
variable_item_set_current_value_index(item, SignalSettingsCounterStepValue);
|
||||
if(cnt_byte_count == 4) {
|
||||
snprintf(text, sizeof(text), "%lX", (unsigned long)counter_value);
|
||||
} else {
|
||||
snprintf(text, sizeof(text), "%X", (unsigned int)(counter_value & 0xffffU));
|
||||
}
|
||||
variable_item_set_current_value_text(item, text);
|
||||
}
|
||||
|
||||
static bool subghz_scene_signal_settings_counter_save(SubGhz* subghz) {
|
||||
FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx);
|
||||
if(!subghz_scene_signal_settings_update_uint32_field(fff, "Cnt", counter_value)) {
|
||||
FURI_LOG_E(TAG, "Error update/insert Cnt value");
|
||||
dialog_message_show_storage_error(subghz->dialogs, "Cannot save\ncounter");
|
||||
return false;
|
||||
}
|
||||
|
||||
subghz_block_generic_global_counter_override_set(counter_value);
|
||||
return subghz_scene_signal_settings_rebuild_save_reload(subghz, false, SUBGHZ_CUSTOM_BTN_OK);
|
||||
}
|
||||
|
||||
static bool subghz_scene_signal_settings_hex_digit(char c, uint8_t* value) {
|
||||
if(c >= '0' && c <= '9') {
|
||||
*value = c - '0';
|
||||
return true;
|
||||
} else if(c >= 'a' && c <= 'f') {
|
||||
*value = c - 'a' + 10;
|
||||
return true;
|
||||
} else if(c >= 'A' && c <= 'F') {
|
||||
*value = c - 'A' + 10;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool subghz_scene_signal_settings_parse_hex_field(
|
||||
const char* text,
|
||||
const char* marker,
|
||||
uint32_t* value,
|
||||
uint8_t* length_bit) {
|
||||
const char* field = strstr(text, marker);
|
||||
if(!field) return false;
|
||||
|
||||
field += strlen(marker);
|
||||
while(*field == ' ' || *field == '\t') {
|
||||
field++;
|
||||
}
|
||||
|
||||
uint32_t parsed = 0;
|
||||
uint8_t digits = 0;
|
||||
uint8_t digit_value = 0;
|
||||
while(digits < 8 && subghz_scene_signal_settings_hex_digit(*field, &digit_value)) {
|
||||
parsed = (parsed << 4) | digit_value;
|
||||
digits++;
|
||||
field++;
|
||||
}
|
||||
|
||||
if(digits == 0) return false;
|
||||
|
||||
*value = parsed;
|
||||
*length_bit = digits * 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void subghz_scene_signal_settings_apply_text_fallback(FuriString* decoded_text) {
|
||||
uint32_t value = 0;
|
||||
uint8_t length_bit = 0;
|
||||
const char* text = furi_string_get_cstr(decoded_text);
|
||||
|
||||
if(!subghz_block_generic_global.cnt_is_available &&
|
||||
subghz_scene_signal_settings_parse_hex_field(text, "Cnt:", &value, &length_bit)) {
|
||||
subghz_block_generic_global.cnt_is_available = true;
|
||||
subghz_block_generic_global.current_cnt = value;
|
||||
subghz_block_generic_global.cnt_length_bit = length_bit;
|
||||
}
|
||||
|
||||
if(!subghz_block_generic_global.btn_is_available &&
|
||||
subghz_scene_signal_settings_parse_hex_field(text, "Btn:", &value, &length_bit)) {
|
||||
subghz_block_generic_global.btn_is_available = true;
|
||||
subghz_block_generic_global.current_btn = value & 0xFF;
|
||||
subghz_block_generic_global.btn_length_bit = length_bit > 8 ? 8 : length_bit;
|
||||
}
|
||||
}
|
||||
|
||||
static void subghz_scene_signal_settings_apply_file_fallback(FlipperFormat* fff) {
|
||||
uint32_t value = 0;
|
||||
|
||||
if(!subghz_block_generic_global.cnt_is_available) {
|
||||
flipper_format_rewind(fff);
|
||||
if(flipper_format_read_uint32(fff, "Cnt", &value, 1)) {
|
||||
subghz_block_generic_global.cnt_is_available = true;
|
||||
subghz_block_generic_global.current_cnt = value;
|
||||
subghz_block_generic_global.cnt_length_bit = 32;
|
||||
}
|
||||
}
|
||||
|
||||
if(!subghz_block_generic_global.btn_is_available) {
|
||||
flipper_format_rewind(fff);
|
||||
if(flipper_format_read_uint32(fff, "Btn", &value, 1)) {
|
||||
subghz_block_generic_global.btn_is_available = true;
|
||||
subghz_block_generic_global.current_btn = value & 0xFF;
|
||||
subghz_block_generic_global.btn_length_bit = 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool subghz_scene_signal_settings_rebuild_save_reload(
|
||||
SubGhz* subghz,
|
||||
bool use_custom_btn,
|
||||
uint8_t custom_btn_id) {
|
||||
const char* file_path = furi_string_get_cstr(subghz->file_path);
|
||||
FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx);
|
||||
|
||||
bool updated = false;
|
||||
int32_t counter_mult = furi_hal_subghz_get_rolling_counter_mult();
|
||||
furi_hal_subghz_set_rolling_counter_mult(0);
|
||||
|
||||
if(use_custom_btn) {
|
||||
subghz_custom_btn_set(custom_btn_id);
|
||||
}
|
||||
|
||||
do {
|
||||
if(!subghz_txrx_rebuild_from_fff(subghz->txrx, fff)) {
|
||||
FURI_LOG_E(TAG, "Error rebuilding protocol data");
|
||||
break;
|
||||
}
|
||||
if(!subghz_save_protocol_to_file(subghz, fff, file_path)) {
|
||||
FURI_LOG_E(TAG, "Error saving edited signal");
|
||||
break;
|
||||
}
|
||||
if(!subghz_key_load(subghz, file_path, false)) {
|
||||
FURI_LOG_E(TAG, "Error reloading edited signal");
|
||||
break;
|
||||
}
|
||||
updated = true;
|
||||
} while(false);
|
||||
|
||||
furi_hal_subghz_set_rolling_counter_mult(counter_mult);
|
||||
|
||||
if(!updated) {
|
||||
dialog_message_show_storage_error(subghz->dialogs, "Cannot save\nsignal");
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
void subghz_scene_signal_settings_counter_mode_changed(VariableItem* item) {
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
@@ -99,6 +401,44 @@ void subghz_scene_signal_settings_counter_mode_changed(VariableItem* item) {
|
||||
}
|
||||
}
|
||||
|
||||
void subghz_scene_signal_settings_counter_changed(VariableItem* item) {
|
||||
if(!cnt_byte_ptr || cnt_byte_count == 0) return;
|
||||
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
if(index == SignalSettingsCounterStepValue) {
|
||||
subghz_scene_signal_settings_counter_update_item(item);
|
||||
return;
|
||||
}
|
||||
|
||||
if(index == SignalSettingsCounterStepUp) {
|
||||
subghz_scene_signal_settings_counter_set_value(counter_value + 1);
|
||||
} else {
|
||||
subghz_scene_signal_settings_counter_set_value(counter_value - 1);
|
||||
}
|
||||
|
||||
subghz_scene_signal_settings_counter_update_item(item);
|
||||
|
||||
SubGhz* subghz = variable_item_get_context(item);
|
||||
furi_assert(subghz);
|
||||
subghz_scene_signal_settings_counter_save(subghz);
|
||||
}
|
||||
|
||||
void subghz_scene_signal_settings_button_changed(VariableItem* item) {
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
if(index >= BUTTON_VALUE_COUNT) index = 0;
|
||||
|
||||
button_custom_id = button_value[index];
|
||||
variable_item_set_current_value_text(
|
||||
item, subghz_scene_signal_settings_get_button_label(button_custom_id));
|
||||
|
||||
if(!button_uses_custom_btn) return;
|
||||
|
||||
SubGhz* subghz = variable_item_get_context(item);
|
||||
furi_assert(subghz);
|
||||
|
||||
subghz_scene_signal_settings_rebuild_save_reload(subghz, true, button_custom_id);
|
||||
}
|
||||
|
||||
void subghz_scene_signal_settings_byte_input_callback(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventByteInputDone);
|
||||
@@ -108,8 +448,10 @@ void subghz_scene_signal_settings_variable_item_list_enter_callback(void* contex
|
||||
SubGhz* subghz = context;
|
||||
|
||||
// when we click OK on "Edit counter" item
|
||||
if(index == 1) {
|
||||
if(index == SignalSettingsIndexCounter) {
|
||||
if(!cnt_byte_ptr || cnt_byte_count == 0) return;
|
||||
submenu_called = 1;
|
||||
furi_string_set_str(byte_input_text, "Enter ");
|
||||
furi_string_cat_printf(byte_input_text, "%i", subghz_block_generic_global.cnt_length_bit);
|
||||
furi_string_cat_str(byte_input_text, "-bits counter in HEX");
|
||||
|
||||
@@ -127,8 +469,10 @@ void subghz_scene_signal_settings_variable_item_list_enter_callback(void* contex
|
||||
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdByteInput);
|
||||
}
|
||||
// when we click OK on "Edit button" item
|
||||
if(index == 2) {
|
||||
if(index == SignalSettingsIndexButton && !button_uses_custom_btn) {
|
||||
if(!btn_byte_ptr || btn_byte_count == 0) return;
|
||||
submenu_called = 2;
|
||||
furi_string_set_str(byte_input_text, "Enter ");
|
||||
furi_string_cat_printf(byte_input_text, "%i", subghz_block_generic_global.btn_length_bit);
|
||||
furi_string_cat_str(byte_input_text, "-bits button in HEX");
|
||||
|
||||
@@ -150,6 +494,22 @@ void subghz_scene_signal_settings_variable_item_list_enter_callback(void* contex
|
||||
void subghz_scene_signal_settings_on_enter(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
|
||||
counter32 = 0;
|
||||
counter16 = 0;
|
||||
cnt_byte_count = 0;
|
||||
cnt_byte_ptr = NULL;
|
||||
counter_value = 0;
|
||||
counter_mask = 0xffffffffUL;
|
||||
button = 0;
|
||||
btn_byte_count = 1;
|
||||
btn_byte_ptr = NULL;
|
||||
submenu_called = 0;
|
||||
button_uses_custom_btn = false;
|
||||
button_custom_id = SUBGHZ_CUSTOM_BTN_OK;
|
||||
subghz_block_generic_global_reset(NULL);
|
||||
subghz_custom_btns_reset();
|
||||
subghz_scene_signal_settings_reset_button_labels();
|
||||
|
||||
// ### Counter mode section ###
|
||||
|
||||
// When we open saved file we do some check and fill up subghz->file_path.
|
||||
@@ -162,6 +522,7 @@ void subghz_scene_signal_settings_on_enter(void* context) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
|
||||
FuriString* tmp_text = furi_string_alloc_set_str("");
|
||||
FuriString* protocol_name = furi_string_alloc();
|
||||
|
||||
uint32_t tmp_counter_mode = 0;
|
||||
counter_mode = 0xff;
|
||||
@@ -174,8 +535,9 @@ void subghz_scene_signal_settings_on_enter(void* context) {
|
||||
FURI_LOG_E(TAG, "Error open file %s", file_path);
|
||||
} else {
|
||||
flipper_format_read_string(fff_data_file, "Protocol", tmp_text);
|
||||
furi_string_set(protocol_name, tmp_text);
|
||||
// compare available protocols names, load CounterMode value from file and setup variable_item_list values_count
|
||||
for(uint8_t i = 0; i < PROTOCOLS_COUNT i++) {
|
||||
for(uint8_t i = 0; i < PROTOCOLS_COUNT; i++) {
|
||||
if(!strcmp(furi_string_get_cstr(tmp_text), protocols[i].name)) {
|
||||
mode_count = protocols[i].mode_count;
|
||||
if(flipper_format_read_uint32(fff_data_file, "CounterMode", &tmp_counter_mode, 1)) {
|
||||
@@ -223,7 +585,10 @@ void subghz_scene_signal_settings_on_enter(void* context) {
|
||||
// deserialaze and decode loaded sugbhz file and push data to subghz_block_generic_global variable
|
||||
if(subghz_protocol_decoder_base_deserialize(decoder, subghz_txrx_get_fff_data(subghz->txrx)) ==
|
||||
SubGhzProtocolStatusOk) {
|
||||
subghz_scene_signal_settings_apply_button_labels(furi_string_get_cstr(protocol_name));
|
||||
subghz_protocol_decoder_base_get_string(decoder, tmp_text);
|
||||
subghz_scene_signal_settings_apply_text_fallback(tmp_text);
|
||||
subghz_scene_signal_settings_apply_file_fallback(subghz_txrx_get_fff_data(subghz->txrx));
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Cant deserialize this subghz file");
|
||||
}
|
||||
@@ -232,55 +597,75 @@ void subghz_scene_signal_settings_on_enter(void* context) {
|
||||
|
||||
if(!subghz_block_generic_global.cnt_is_available) {
|
||||
counter_mode = 0xff;
|
||||
furi_string_set_str(tmp_text, "-");
|
||||
FURI_LOG_D(TAG, "Counter mode and edit not available for this protocol");
|
||||
} else {
|
||||
counter_not_available = false;
|
||||
counter_mask = subghz_scene_signal_settings_counter_get_mask();
|
||||
|
||||
// Check is there byte_count more than 2 hex bytes long or not
|
||||
// To show hex value we must correct revert bytes for ByteInput view with __bswapХХ
|
||||
// ByteInput stores the visible hex value as big-endian bytes.
|
||||
if(subghz_block_generic_global.cnt_length_bit > 16) {
|
||||
counter32 = subghz_block_generic_global.current_cnt;
|
||||
furi_string_printf(tmp_text, "%lX", counter32);
|
||||
counter32 = __bswap32(counter32);
|
||||
cnt_byte_ptr = (uint8_t*)&counter32;
|
||||
cnt_byte_count = 4;
|
||||
} else {
|
||||
counter16 = subghz_block_generic_global.current_cnt;
|
||||
furi_string_printf(tmp_text, "%X", counter16);
|
||||
counter16 = __bswap16(counter16);
|
||||
cnt_byte_ptr = (uint8_t*)&counter16;
|
||||
cnt_byte_count = 2;
|
||||
}
|
||||
subghz_scene_signal_settings_counter_set_value(subghz_block_generic_global.current_cnt);
|
||||
subghz_scene_signal_settings_counter_format(tmp_text);
|
||||
}
|
||||
|
||||
item = variable_item_list_add(variable_item_list, "Edit Counter", 1, NULL, subghz);
|
||||
variable_item_set_current_value_index(item, 0);
|
||||
item = variable_item_list_add(
|
||||
variable_item_list,
|
||||
"Edit Counter",
|
||||
counter_not_available ? 1 : SignalSettingsCounterStepCount,
|
||||
counter_not_available ? NULL : subghz_scene_signal_settings_counter_changed,
|
||||
subghz);
|
||||
variable_item_set_current_value_index(
|
||||
item, counter_not_available ? 0 : SignalSettingsCounterStepValue);
|
||||
variable_item_set_current_value_text(item, furi_string_get_cstr(tmp_text));
|
||||
variable_item_set_locked(item, (counter_not_available), "Not available\nfor this\nprotocol !");
|
||||
//
|
||||
|
||||
// ### Button edit section ###
|
||||
|
||||
if(!subghz_block_generic_global.btn_is_available) {
|
||||
if(subghz_custom_btn_is_allowed()) {
|
||||
uint8_t max_custom_btn = subghz_custom_btn_get_max();
|
||||
uint8_t custom_button_count = max_custom_btn + 1;
|
||||
if(custom_button_count > BUTTON_VALUE_COUNT) custom_button_count = BUTTON_VALUE_COUNT;
|
||||
button_uses_custom_btn = custom_button_count > 1;
|
||||
}
|
||||
|
||||
if(button_uses_custom_btn) {
|
||||
button_not_available = false;
|
||||
furi_string_set_str(
|
||||
tmp_text, subghz_scene_signal_settings_get_button_label(SUBGHZ_CUSTOM_BTN_OK));
|
||||
} else if(!subghz_block_generic_global.btn_is_available) {
|
||||
furi_string_set_str(tmp_text, "-");
|
||||
FURI_LOG_D(TAG, "Button edit not available for this protocol");
|
||||
} else {
|
||||
button_not_available = false;
|
||||
button = subghz_block_generic_global.current_btn;
|
||||
furi_string_printf(tmp_text, "%X", button);
|
||||
btn_byte_ptr = (uint8_t*)&button;
|
||||
furi_string_printf(tmp_text, "%X", button);
|
||||
}
|
||||
|
||||
item = variable_item_list_add(variable_item_list, "Edit Button", 1, NULL, subghz);
|
||||
uint8_t button_count = 1;
|
||||
if(button_uses_custom_btn) {
|
||||
button_count = subghz_custom_btn_get_max() + 1;
|
||||
if(button_count > BUTTON_VALUE_COUNT) button_count = BUTTON_VALUE_COUNT;
|
||||
}
|
||||
item = variable_item_list_add(
|
||||
variable_item_list,
|
||||
button_uses_custom_btn ? "Button" : "Edit Button",
|
||||
button_count,
|
||||
button_uses_custom_btn ? subghz_scene_signal_settings_button_changed : NULL,
|
||||
subghz);
|
||||
variable_item_set_current_value_index(item, 0);
|
||||
variable_item_set_current_value_text(item, furi_string_get_cstr(tmp_text));
|
||||
variable_item_set_locked(item, (button_not_available), "Not available\nfor this\nprotocol !");
|
||||
//
|
||||
|
||||
furi_assert(cnt_byte_ptr);
|
||||
furi_assert(cnt_byte_count > 0);
|
||||
furi_assert(btn_byte_ptr);
|
||||
|
||||
furi_string_free(tmp_text);
|
||||
furi_string_free(protocol_name);
|
||||
|
||||
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList);
|
||||
}
|
||||
@@ -290,23 +675,21 @@ bool subghz_scene_signal_settings_on_event(void* context, SceneManagerEvent even
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubGhzCustomEventByteInputDone) {
|
||||
FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx);
|
||||
|
||||
switch(submenu_called) {
|
||||
// edit counter
|
||||
case 1:
|
||||
switch(cnt_byte_count) {
|
||||
case 2:
|
||||
// set new cnt value and override_flag to global variable and call transmit to generate and save subghz signal
|
||||
counter16 = __bswap16(counter16);
|
||||
subghz_block_generic_global_counter_override_set(counter16);
|
||||
subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx));
|
||||
subghz_txrx_stop(subghz->txrx);
|
||||
subghz_scene_signal_settings_counter_set_value(counter16);
|
||||
subghz_scene_signal_settings_counter_save(subghz);
|
||||
break;
|
||||
case 4:
|
||||
// the same for 32 bit Counter
|
||||
counter32 = __bswap32(counter32);
|
||||
subghz_block_generic_global_counter_override_set(counter32);
|
||||
subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx));
|
||||
subghz_txrx_stop(subghz->txrx);
|
||||
subghz_scene_signal_settings_counter_set_value(counter32);
|
||||
subghz_scene_signal_settings_counter_save(subghz);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -314,14 +697,10 @@ bool subghz_scene_signal_settings_on_event(void* context, SceneManagerEvent even
|
||||
break;
|
||||
// edit button
|
||||
case 2:
|
||||
subghz_scene_signal_settings_update_uint32_field(fff, "Btn", button);
|
||||
subghz_block_generic_global_button_override_set(button);
|
||||
// save counter mult to rewrite subghz singnal without changing counter
|
||||
int32_t tmp_counter = furi_hal_subghz_get_rolling_counter_mult();
|
||||
furi_hal_subghz_set_rolling_counter_mult(0);
|
||||
subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx));
|
||||
subghz_txrx_stop(subghz->txrx);
|
||||
// restore counter mult
|
||||
furi_hal_subghz_set_rolling_counter_mult(tmp_counter);
|
||||
subghz_scene_signal_settings_rebuild_save_reload(
|
||||
subghz, false, SUBGHZ_CUSTOM_BTN_OK);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -330,13 +709,10 @@ bool subghz_scene_signal_settings_on_event(void* context, SceneManagerEvent even
|
||||
|
||||
scene_manager_previous_scene(subghz->scene_manager);
|
||||
return true;
|
||||
|
||||
} else {
|
||||
if(event.type == SceneManagerEventTypeBack) {
|
||||
scene_manager_previous_scene(subghz->scene_manager);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
scene_manager_previous_scene(subghz->scene_manager);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -352,4 +728,5 @@ void subghz_scene_signal_settings_on_exit(void* context) {
|
||||
byte_input_set_result_callback(subghz->byte_input, NULL, NULL, NULL, NULL, 0);
|
||||
byte_input_set_header_text(subghz->byte_input, "");
|
||||
furi_string_free(byte_input_text);
|
||||
subghz_custom_btns_reset();
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_LED_AND_POWER_AMP "LedAndPowerAmp"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_TX_POWER "TXPower"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_CUSTOM_CAR_EMULATE "CustomCarEmulate"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_PROTOCOL_FILTER "ProtocolFilter"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_PROTOCOL_FILTER "ProtocolFilterOff"
|
||||
|
||||
SubGhzLastSettings* subghz_last_settings_alloc(void) {
|
||||
SubGhzLastSettings* instance = malloc(sizeof(SubGhzLastSettings));
|
||||
@@ -187,6 +187,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count
|
||||
flipper_format_rewind(fff_data_file);
|
||||
}
|
||||
furi_string_free(filter_str);
|
||||
subghz_last_settings_protocol_filter_normalize(instance);
|
||||
|
||||
} while(0);
|
||||
} else {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <furi_hal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <storage/storage.h>
|
||||
#include <lib/subghz/types.h>
|
||||
|
||||
@@ -13,6 +14,7 @@
|
||||
#define SUBGHZ_LAST_SETTING_DEFAULT_FREQUENCY 433920000
|
||||
#define SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL 2
|
||||
#define SUBGHZ_LAST_SETTING_DEFAULT_PRESET_HOPPING_THRESHOLD (-80.0f)
|
||||
#define SUBGHZ_LAST_SETTINGS_PROTOCOL_FILTER_SIZE 1024
|
||||
|
||||
typedef struct {
|
||||
uint32_t frequency;
|
||||
@@ -31,9 +33,196 @@ typedef struct {
|
||||
bool leds_and_amp;
|
||||
uint8_t tx_power;
|
||||
bool custom_car_emulate;
|
||||
char protocol_filter[256]; /* comma-separated allowlist, empty = disabled */
|
||||
char protocol_filter[SUBGHZ_LAST_SETTINGS_PROTOCOL_FILTER_SIZE]; /* comma-separated disabled protocols, empty = all enabled */
|
||||
} SubGhzLastSettings;
|
||||
|
||||
static inline void subghz_last_settings_protocol_filter_next_token(
|
||||
const char** cursor,
|
||||
const char** token,
|
||||
size_t* token_len) {
|
||||
const char* start = *cursor;
|
||||
while((*start == ',') || (*start == ' ') || (*start == '\t')) {
|
||||
start++;
|
||||
}
|
||||
|
||||
const char* end = start;
|
||||
while((*end != '\0') && (*end != ',')) {
|
||||
end++;
|
||||
}
|
||||
|
||||
const char* trim_end = end;
|
||||
while((trim_end > start) && ((trim_end[-1] == ' ') || (trim_end[-1] == '\t'))) {
|
||||
trim_end--;
|
||||
}
|
||||
|
||||
*token = start;
|
||||
*token_len = (size_t)(trim_end - start);
|
||||
*cursor = (*end == ',') ? end + 1 : end;
|
||||
}
|
||||
|
||||
static inline bool subghz_last_settings_protocol_filter_token_matches(
|
||||
const char* token,
|
||||
size_t token_len,
|
||||
const char* name,
|
||||
size_t name_len) {
|
||||
return (token_len == name_len) && (strncmp(token, name, token_len) == 0);
|
||||
}
|
||||
|
||||
static inline bool subghz_last_settings_protocol_filter_contains_token(
|
||||
const char* filter,
|
||||
const char* token,
|
||||
size_t token_len) {
|
||||
const char* cursor = filter;
|
||||
const char* current = NULL;
|
||||
size_t current_len = 0;
|
||||
|
||||
while(*cursor != '\0') {
|
||||
subghz_last_settings_protocol_filter_next_token(&cursor, ¤t, ¤t_len);
|
||||
if((current_len != 0) &&
|
||||
subghz_last_settings_protocol_filter_token_matches(
|
||||
current, current_len, token, token_len)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool subghz_last_settings_protocol_filter_append_token(
|
||||
char* filter,
|
||||
size_t filter_size,
|
||||
const char* token,
|
||||
size_t token_len) {
|
||||
if(token_len == 0) return true;
|
||||
|
||||
size_t filter_len = strlen(filter);
|
||||
size_t separator_len = filter_len == 0 ? 0 : 1;
|
||||
if((filter_len + separator_len + token_len) >= filter_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(separator_len != 0) {
|
||||
filter[filter_len++] = ',';
|
||||
}
|
||||
memcpy(&filter[filter_len], token, token_len);
|
||||
filter[filter_len + token_len] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool subghz_last_settings_protocol_filter_contains(
|
||||
const SubGhzLastSettings* instance,
|
||||
const char* protocol) {
|
||||
if((instance == NULL) || (protocol == NULL) || (protocol[0] == '\0')) return false;
|
||||
|
||||
return subghz_last_settings_protocol_filter_contains_token(
|
||||
instance->protocol_filter, protocol, strlen(protocol));
|
||||
}
|
||||
|
||||
static inline bool subghz_last_settings_protocol_filter_normalize(
|
||||
SubGhzLastSettings* instance) {
|
||||
if(instance == NULL) return false;
|
||||
|
||||
char normalized[SUBGHZ_LAST_SETTINGS_PROTOCOL_FILTER_SIZE] = {0};
|
||||
const char* cursor = instance->protocol_filter;
|
||||
const char* token = NULL;
|
||||
size_t token_len = 0;
|
||||
|
||||
while(*cursor != '\0') {
|
||||
subghz_last_settings_protocol_filter_next_token(&cursor, &token, &token_len);
|
||||
if((token_len == 0) ||
|
||||
subghz_last_settings_protocol_filter_contains_token(normalized, token, token_len)) {
|
||||
continue;
|
||||
}
|
||||
if(!subghz_last_settings_protocol_filter_append_token(
|
||||
normalized, sizeof(normalized), token, token_len)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool changed = strcmp(instance->protocol_filter, normalized) != 0;
|
||||
if(changed) {
|
||||
memcpy(instance->protocol_filter, normalized, sizeof(instance->protocol_filter));
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
static inline bool subghz_last_settings_protocol_filter_set(
|
||||
SubGhzLastSettings* instance,
|
||||
const char* protocol,
|
||||
bool disabled) {
|
||||
if((instance == NULL) || (protocol == NULL) || (protocol[0] == '\0')) return false;
|
||||
|
||||
char updated[SUBGHZ_LAST_SETTINGS_PROTOCOL_FILTER_SIZE] = {0};
|
||||
const char* cursor = instance->protocol_filter;
|
||||
const char* token = NULL;
|
||||
size_t token_len = 0;
|
||||
const size_t protocol_len = strlen(protocol);
|
||||
bool protocol_written = false;
|
||||
|
||||
while(*cursor != '\0') {
|
||||
subghz_last_settings_protocol_filter_next_token(&cursor, &token, &token_len);
|
||||
if(token_len == 0) continue;
|
||||
|
||||
bool is_target = subghz_last_settings_protocol_filter_token_matches(
|
||||
token, token_len, protocol, protocol_len);
|
||||
if(is_target) {
|
||||
if(disabled && !protocol_written) {
|
||||
if(!subghz_last_settings_protocol_filter_append_token(
|
||||
updated, sizeof(updated), protocol, protocol_len)) {
|
||||
return false;
|
||||
}
|
||||
protocol_written = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!subghz_last_settings_protocol_filter_contains_token(updated, token, token_len)) {
|
||||
if(!subghz_last_settings_protocol_filter_append_token(
|
||||
updated, sizeof(updated), token, token_len)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(disabled && !protocol_written) {
|
||||
if(!subghz_last_settings_protocol_filter_append_token(
|
||||
updated, sizeof(updated), protocol, protocol_len)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool changed = strcmp(instance->protocol_filter, updated) != 0;
|
||||
if(changed) {
|
||||
memcpy(instance->protocol_filter, updated, sizeof(instance->protocol_filter));
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
static inline uint8_t subghz_last_settings_protocol_filter_count(
|
||||
const SubGhzLastSettings* instance) {
|
||||
if(instance == NULL) return 0;
|
||||
|
||||
char seen[SUBGHZ_LAST_SETTINGS_PROTOCOL_FILTER_SIZE] = {0};
|
||||
const char* cursor = instance->protocol_filter;
|
||||
const char* token = NULL;
|
||||
size_t token_len = 0;
|
||||
uint8_t count = 0;
|
||||
|
||||
while(*cursor != '\0') {
|
||||
subghz_last_settings_protocol_filter_next_token(&cursor, &token, &token_len);
|
||||
if((token_len == 0) ||
|
||||
subghz_last_settings_protocol_filter_contains_token(seen, token, token_len)) {
|
||||
continue;
|
||||
}
|
||||
if(!subghz_last_settings_protocol_filter_append_token(seen, sizeof(seen), token, token_len)) {
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
SubGhzLastSettings* subghz_last_settings_alloc(void);
|
||||
|
||||
void subghz_last_settings_free(SubGhzLastSettings* instance);
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to the 24cxxprog EEPROM Programmer application will be documented in this file.
|
||||
|
||||
## [2.0.0] - 2026-03-11
|
||||
|
||||
### 🚀 Major Features Added
|
||||
|
||||
#### Dynamic Memory Support for All 24Cxx Chips
|
||||
- **Full chip type support**: Added complete support for all EEPROM sizes from 24C01 (128B) to 24C512 (64KB)
|
||||
- **Dynamic buffer allocation**: Memory buffers now automatically resize based on selected chip type
|
||||
- **Configurable in Settings**: Users can now select chip type in Settings menu, and all operations adapt automatically
|
||||
|
||||
### ✨ Enhancements
|
||||
|
||||
#### Memory Management
|
||||
- Replaced fixed 256-byte buffers with dynamic allocation:
|
||||
- `memory_data` - dynamically allocated based on chip size
|
||||
- `file_data` - dynamically allocated based on chip size
|
||||
- `verify_buffer` - dynamically allocated based on chip size
|
||||
- Added `get_eeprom_size()` helper function returning size in bytes for each chip type
|
||||
- Added `reallocate_buffers()` function for automatic buffer reallocation on chip type change
|
||||
- Memory size tracked in `memory_size` field (32-bit for chips up to 64KB)
|
||||
|
||||
#### Read/Write/Erase Operations
|
||||
- **Read operation**: Now reads entire EEPROM regardless of size (128B to 64KB)
|
||||
- **Write operation**: Supports writing to full address range of selected chip
|
||||
- **Erase operation**: Clears entire memory of selected chip type
|
||||
- **File operations**: Binary dumps now save/load full chip capacity
|
||||
|
||||
#### User Interface Improvements
|
||||
- Address display format adapts to memory size:
|
||||
- Small chips (≤256B): `0x00` format
|
||||
- Large chips (>256B): `0000` hex format (4 digits)
|
||||
- Progress indicators updated for all memory sizes
|
||||
- Navigation (Up/Down) works across entire address range
|
||||
- File size display shows actual chip capacity
|
||||
|
||||
#### File Naming
|
||||
- Filename generation now includes all chip types:
|
||||
- Examples: `24C01_2026-03-11_10-30.bin`, `24C256_2026-03-11_10-30.bin`
|
||||
- Automatic timestamp-based naming for all chip variants
|
||||
|
||||
### 🔧 Technical Changes
|
||||
|
||||
#### Type Updates
|
||||
- Changed address/size types from `uint8_t` to `uint32_t` for large memory support:
|
||||
- `current_address`: now `uint32_t`
|
||||
- `read_total_bytes`: now `uint32_t`
|
||||
- `write_total_bytes_async`: now `uint32_t`
|
||||
- `verify_total_bytes`: now `uint32_t`
|
||||
- `erase_current_addr`: now `uint32_t`
|
||||
- `progress_value`: now `uint32_t`
|
||||
- `file_size`: now `uint32_t`
|
||||
|
||||
#### Format Specifiers
|
||||
- Updated all `printf`/`snprintf` calls to use correct format for `uint32_t`:
|
||||
- Changed `%d` to `%lu` for unsigned long
|
||||
- Changed `%X` to `%lX` for hex unsigned long
|
||||
|
||||
#### Memory Safety
|
||||
- Added proper memory initialization in `reallocate_buffers()`
|
||||
- Added null pointer checks for all dynamically allocated buffers
|
||||
- Proper cleanup in `eeprom_app_free()` - all buffers freed correctly
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
- Fixed buffer overflow risk in memory operations for larger chips
|
||||
- Fixed format specifier warnings causing compilation errors
|
||||
- Fixed address boundary checking for chips larger than 256 bytes
|
||||
- Fixed progress bar calculations for larger memory sizes
|
||||
|
||||
### 🔄 Behavioral Changes
|
||||
- Settings → Chip Type now immediately reallocates buffers
|
||||
- Current address is reset to 0 if it exceeds new chip size after type change
|
||||
- File load operation respects maximum chip capacity (won't load more than chip can hold)
|
||||
|
||||
### 📋 Supported Chip Types
|
||||
|
||||
Complete support matrix:
|
||||
| Chip Type | Size | Status |
|
||||
|-----------|------|--------|
|
||||
| 24C01 | 128 bytes | ✅ Full Support |
|
||||
| 24C02 | 256 bytes | ✅ Full Support |
|
||||
| 24C04 | 512 bytes | ✅ Full Support |
|
||||
| 24C08 | 1 KB | ✅ Full Support |
|
||||
| 24C16 | 2 KB | ✅ Full Support |
|
||||
| 24C32 | 4 KB | ✅ Full Support |
|
||||
| 24C64 | 8 KB | ✅ Full Support |
|
||||
| 24C128 | 16 KB | ✅ Full Support |
|
||||
| 24C256 | 32 KB | ✅ Full Support |
|
||||
| 24C512 | 64 KB | ✅ Full Support |
|
||||
|
||||
### ⚠️ Breaking Changes
|
||||
- Binary dump files from previous versions (always 256 bytes) are incompatible with chip-specific sizes
|
||||
- Users should re-read and save new dumps after upgrading
|
||||
|
||||
---
|
||||
|
||||
## [1.0.0] - Previous Version
|
||||
|
||||
### Initial Release
|
||||
- Basic read/write/erase operations
|
||||
- Fixed 256-byte buffer (24C02 only)
|
||||
- I2C address configuration
|
||||
- File load/save operations
|
||||
- Basic hex viewer
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Dr.Mosfet
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,222 @@
|
||||
# 🔧 24cxxprog - EEPROM 24Cxx Programmer
|
||||
|
||||
<h2 align="center">A Comprehensive EEPROM Programmer for Flipper Zero</h2>
|
||||
|
||||
<div align="center">
|
||||
<table style="width:100%; border:none;">
|
||||
<tr style="border:none;">
|
||||
<td style="border:none; padding:10px;">
|
||||
<img src="screenshots/1.png" alt="Main Menu - Operations" style="width:100%;">
|
||||
<br>
|
||||
<em>Menu główne z operacjami (Odczyt, Zapis, Kasowanie)</em>
|
||||
</td>
|
||||
<td style="border:none; padding:10px;">
|
||||
<img src="screenshots/2.png" alt="Configuration Menu" style="width:100%;">
|
||||
<br>
|
||||
<em>Menu konfiguracji (Adres I2C, Rozmiar pamięci)</em>
|
||||
</td>
|
||||
<td style="border:none; padding:10px;">
|
||||
<img src="screenshots/3.png" alt="Data Display - EEPROM Contents" style="width:100%;">
|
||||
<br>
|
||||
<em>Wyświetlanie zawartości EEPROM (Dane heksadecymalne)</em>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
This is a **comprehensive EEPROM programmer application** designed for the **Flipper Zero** that interfaces with the **24Cxx series I2C memory chips**. The application provides a complete suite of tools for reading, writing, erasing, and managing EEPROM memory with a user-friendly interface on the Flipper's screen.
|
||||
|
||||
## ✨ Features Overview
|
||||
|
||||
### 📝 EEPROM Operations
|
||||
|
||||
Complete toolset for memory management:
|
||||
|
||||
* **Read Operations:** View complete EEPROM contents with address and hexadecimal data display.
|
||||
* **Write Operations:** Program custom data into specific memory addresses.
|
||||
* **Erase Functions:** Clear individual bytes, pages, or entire memory sections.
|
||||
* **Dump to Storage:** Export EEPROM contents to Flipper SD card for backup and analysis.
|
||||
* **Restore from Backup:** Load previously saved EEPROM data back into the chip.
|
||||
|
||||
### 🎨 User Interface & Experience
|
||||
|
||||
Intuitive interface optimized for Flipper Zero's display:
|
||||
|
||||
* **Main Menu:** Clear operation selection with visual feedback.
|
||||
* **Data Viewer:** Scrollable hex display showing actual EEPROM contents.
|
||||
* **Configuration Menu:** Easy access to sensor parameters and device settings.
|
||||
* **Address Navigation:** Precise control over memory location selection.
|
||||
* **Progress Indicator:** Real-time feedback during long operations.
|
||||
|
||||
### ⚙️ Configuration Options
|
||||
|
||||
Customize the programmer for your specific hardware:
|
||||
|
||||
* **I2C Address Selection:** Choose between multiple I2C addresses (**0x50-0x57**) for different chip variants.
|
||||
* **Memory Size Selection:** Automatically detect or manually set chip capacity (**1KB to 64KB** and larger).
|
||||
* **Page Size Configuration:** Adapt to different chip architectures (**8 bytes to 256 bytes per page**).
|
||||
* **Persistent Settings:** Configurations are automatically saved for quick access.
|
||||
|
||||
### 💻 Technical Features & Robustness
|
||||
|
||||
Built for reliability on the Flipper Zero platform:
|
||||
|
||||
* **I2C Protocol Support:** Robust communication with error checking.
|
||||
* **Address Validation:** Prevents out-of-bounds memory access.
|
||||
* **Timeout Protection:** Safeguards against communication errors.
|
||||
* **Error Handling:** Comprehensive error messages for troubleshooting.
|
||||
* **Non-blocking Operations:** Responsive UI that doesn't freeze during I2C transactions.
|
||||
* **Data Verification:** Verify written data integrity after programming.
|
||||
|
||||
## 🔋 Supported 24Cxx Chips
|
||||
|
||||
Comprehensive support for the entire 24Cxx family:
|
||||
|
||||
<table style="width:100%; border:1px solid #ddd; border-collapse: collapse; text-align: left;">
|
||||
<thead style="background-color: #f8f8f8;">
|
||||
<tr>
|
||||
<th style="padding: 8px; border:1px solid #ddd;">Chip Model</th>
|
||||
<th style="padding: 8px; border:1px solid #ddd;">Memory Size</th>
|
||||
<th style="padding: 8px; border:1px solid #ddd;">Page Size</th>
|
||||
<th style="padding: 8px; border:1px solid #ddd;">Address Range</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="padding: 8px; border:1px solid #ddd;"><strong>24C01</strong></td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">128 Bytes</td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">8 Bytes</td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">0x00 - 0x7F</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 8px; border:1px solid #ddd;"><strong>24C02</strong></td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">256 Bytes</td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">8 Bytes</td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">0x00 - 0xFF</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 8px; border:1px solid #ddd;"><strong>24C04 - 24C16</strong></td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">512B - 2KB</td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">16 Bytes</td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">0x00 - 0xFFFF</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 8px; border:1px solid #ddd;"><strong>24C32 - 24C64</strong></td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">4KB - 8KB</td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">32 Bytes</td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">0x0000 - 0x1FFF</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 8px; border:1px solid #ddd;"><strong>24C128 - 24C512</strong></td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">16KB - 64KB</td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">64 Bytes</td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">0x0000 - 0xFFFF</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
---
|
||||
|
||||
## 🕹️ Navigation Guide
|
||||
|
||||
<table style="width:100%; border:1px solid #ddd; border-collapse: collapse; text-align: left;">
|
||||
<thead style="background-color: #f8f8f8;">
|
||||
<tr>
|
||||
<th style="padding: 8px; border:1px solid #ddd;">Screen</th>
|
||||
<th style="padding: 8px; border:1px solid #ddd;">D-Pad Up/Down</th>
|
||||
<th style="padding: 8px; border:1px solid #ddd;">D-Pad Left/Right</th>
|
||||
<th style="padding: 8px; border:1px solid #ddd;">OK Button</th>
|
||||
<th style="padding: 8px; border:1px solid #ddd;">Back Button</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="padding: 8px; border:1px solid #ddd;"><strong>Main Menu</strong></td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">Browse operations (Read, Write, Erase, Dump, Restore)</td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">-</td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">Select operation</td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;"><strong>Exit</strong> application</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 8px; border:1px solid #ddd;"><strong>Read/Write</strong></td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">Navigate through addresses</td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">Adjust byte values (Write mode)</td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">Confirm operation</td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">Return to Main Menu</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 8px; border:1px solid #ddd;"><strong>Configuration</strong></td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">Navigate between settings</td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">Adjust parameter values</td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">Apply settings</td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">Cancel and return</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 8px; border:1px solid #ddd;"><strong>Data View</strong></td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">Scroll data up/down</td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">Jump to address</td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">Show hex/ASCII toggle</td>
|
||||
<td style="padding: 8px; border:1px solid #ddd;">Exit data view</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## 🔌 Hardware Connections
|
||||
|
||||
Standard I2C pinout for Flipper Zero GPIO:
|
||||
|
||||
```
|
||||
24Cxx EEPROM Module Flipper Zero GPIO
|
||||
───────────────── ─────────────────
|
||||
SDA (Pin 5) ───→ GPIO_SDA (Pin 16)
|
||||
SCL (Pin 6) ───→ GPIO_SCL (Pin 15)
|
||||
GND (Pin 4) ───→ GND (Pin 8)
|
||||
VCC (Pin 8) ───→ 3.3V (Pin 9)
|
||||
|
||||
Optional Pull-ups: 4.7kΩ from SDA and SCL to 3.3V
|
||||
```
|
||||
|
||||
## 📋 Operation Details
|
||||
|
||||
### Read
|
||||
- Displays EEPROM contents in hexadecimal format
|
||||
- Shows address, data bytes, and ASCII representation
|
||||
- Scrollable for chips larger than display capacity
|
||||
|
||||
### Write
|
||||
- Enter target address and data values
|
||||
- Supports single byte or page programming
|
||||
- Automatic write cycle delay handling
|
||||
|
||||
### Erase
|
||||
- Clear individual bytes to 0xFF
|
||||
- Erase entire pages
|
||||
- Full chip erase with confirmation
|
||||
|
||||
### Dump
|
||||
- Export EEPROM to **`/ext/apps_data/24cxxprog/`** directory
|
||||
- Creates timestamped backup files
|
||||
- Preserves complete memory state
|
||||
|
||||
### Restore
|
||||
- Load previously dumped EEPROM data
|
||||
- Verify before writing
|
||||
- Restore to specified starting address
|
||||
|
||||
---
|
||||
|
||||
## 👨💻 Developer
|
||||
|
||||
This application was created by **Dr. Mosfet** for the Flipper Zero community.
|
||||
|
||||
**Repository:** [kamylwnb/24cxxprog](https://github.com/kamylwnb/24cxxprog)
|
||||
|
||||
**Version:** 1.0
|
||||
**Category:** GPIO / Tools
|
||||
**Platform:** Flipper Zero F7
|
||||
|
||||
---
|
||||
|
||||
**Happy EEPROM programming! 🔧**
|
||||
@@ -0,0 +1,22 @@
|
||||
App(
|
||||
appid="24cxxprog",
|
||||
name="24Cxx Programmer",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="eeprom_app_24cxx",
|
||||
cdefines=["APP_24CXXPROG"],
|
||||
requires=[
|
||||
"gui",
|
||||
"i2c",
|
||||
],
|
||||
sources=[
|
||||
"i2c_24c02_app.cpp",
|
||||
"i2c_24c02.cpp",
|
||||
],
|
||||
stack_size=2 * 1024,
|
||||
order=21,
|
||||
fap_icon="icons/ikon.png",
|
||||
fap_category="GPIO",
|
||||
fap_author="@Dr.Mosfet",
|
||||
fap_version="2.0",
|
||||
fap_description="EEPROM 24Cxx programmer via I2C with read, write, erase and dump/restore options.",
|
||||
)
|
||||
@@ -0,0 +1,212 @@
|
||||
#include "i2c_24c02.hpp"
|
||||
#include "furi_hal_i2c.h"
|
||||
#include <furi.h>
|
||||
|
||||
EEPROM24C02::EEPROM24C02(uint8_t i2c_address_7bit)
|
||||
: _i2c_addr_8bit(i2c_address_7bit << 1) {
|
||||
}
|
||||
|
||||
bool EEPROM24C02::init() {
|
||||
// Just check if device is responding
|
||||
return isAvailable();
|
||||
}
|
||||
|
||||
bool EEPROM24C02::isAvailable() {
|
||||
furi_hal_i2c_acquire(&furi_hal_i2c_handle_external);
|
||||
|
||||
// Try to read a dummy byte to check if device responds
|
||||
uint8_t dummy_data;
|
||||
bool success = furi_hal_i2c_rx(
|
||||
&furi_hal_i2c_handle_external, _i2c_addr_8bit, &dummy_data, 1, EEPROM_I2C_TIMEOUT);
|
||||
|
||||
furi_hal_i2c_release(&furi_hal_i2c_handle_external);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool EEPROM24C02::readByte(uint8_t memory_addr, uint8_t& data) {
|
||||
furi_hal_i2c_acquire(&furi_hal_i2c_handle_external);
|
||||
|
||||
// Send memory address first
|
||||
bool success_tx = furi_hal_i2c_tx_ext(
|
||||
&furi_hal_i2c_handle_external,
|
||||
_i2c_addr_8bit,
|
||||
false,
|
||||
&memory_addr,
|
||||
1,
|
||||
FuriHalI2cBeginStart,
|
||||
FuriHalI2cEndAwaitRestart,
|
||||
EEPROM_I2C_TIMEOUT);
|
||||
|
||||
// Read the data
|
||||
bool success_rx = false;
|
||||
if(success_tx) {
|
||||
success_rx = furi_hal_i2c_rx_ext(
|
||||
&furi_hal_i2c_handle_external,
|
||||
_i2c_addr_8bit,
|
||||
false,
|
||||
&data,
|
||||
1,
|
||||
FuriHalI2cBeginRestart,
|
||||
FuriHalI2cEndStop,
|
||||
EEPROM_I2C_TIMEOUT);
|
||||
}
|
||||
|
||||
furi_hal_i2c_release(&furi_hal_i2c_handle_external);
|
||||
|
||||
return success_tx && success_rx;
|
||||
}
|
||||
|
||||
bool EEPROM24C02::writeByte(uint8_t memory_addr, uint8_t data) {
|
||||
uint8_t write_buffer[2];
|
||||
write_buffer[0] = memory_addr;
|
||||
write_buffer[1] = data;
|
||||
|
||||
furi_hal_i2c_acquire(&furi_hal_i2c_handle_external);
|
||||
|
||||
bool success = furi_hal_i2c_tx_ext(
|
||||
&furi_hal_i2c_handle_external,
|
||||
_i2c_addr_8bit,
|
||||
false,
|
||||
write_buffer,
|
||||
2,
|
||||
FuriHalI2cBeginStart,
|
||||
FuriHalI2cEndStop,
|
||||
EEPROM_I2C_TIMEOUT);
|
||||
|
||||
furi_hal_i2c_release(&furi_hal_i2c_handle_external);
|
||||
|
||||
if(success) {
|
||||
// Wait for write cycle to complete (typically 5ms for 24C02)
|
||||
furi_delay_ms(10);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool EEPROM24C02::readBytes(uint8_t start_addr, uint8_t* buffer, uint8_t length) {
|
||||
if(length == 0 || buffer == nullptr) return false;
|
||||
|
||||
furi_hal_i2c_acquire(&furi_hal_i2c_handle_external);
|
||||
|
||||
// Send start address
|
||||
bool success_tx = furi_hal_i2c_tx_ext(
|
||||
&furi_hal_i2c_handle_external,
|
||||
_i2c_addr_8bit,
|
||||
false,
|
||||
&start_addr,
|
||||
1,
|
||||
FuriHalI2cBeginStart,
|
||||
FuriHalI2cEndAwaitRestart,
|
||||
EEPROM_I2C_TIMEOUT);
|
||||
|
||||
// Sequential read
|
||||
bool success_rx = false;
|
||||
if(success_tx) {
|
||||
success_rx = furi_hal_i2c_rx_ext(
|
||||
&furi_hal_i2c_handle_external,
|
||||
_i2c_addr_8bit,
|
||||
false,
|
||||
buffer,
|
||||
length,
|
||||
FuriHalI2cBeginRestart,
|
||||
FuriHalI2cEndStop,
|
||||
EEPROM_I2C_TIMEOUT);
|
||||
}
|
||||
|
||||
furi_hal_i2c_release(&furi_hal_i2c_handle_external);
|
||||
|
||||
return success_tx && success_rx;
|
||||
}
|
||||
|
||||
bool EEPROM24C02::writeBytes(uint8_t start_addr, const uint8_t* buffer, uint8_t length) {
|
||||
if(length == 0 || buffer == nullptr) return false;
|
||||
|
||||
// 24C02 has 8-byte page size - we need to handle page boundaries
|
||||
uint8_t bytes_written = 0;
|
||||
|
||||
while(bytes_written < length) {
|
||||
uint8_t current_addr = start_addr + bytes_written;
|
||||
uint8_t page_offset = current_addr % EEPROM_24C02_PAGE_SIZE;
|
||||
uint8_t bytes_in_page = EEPROM_24C02_PAGE_SIZE - page_offset;
|
||||
uint8_t bytes_to_write =
|
||||
(length - bytes_written < bytes_in_page) ? (length - bytes_written) : bytes_in_page;
|
||||
|
||||
// Prepare write buffer for this page
|
||||
uint8_t write_buffer[EEPROM_24C02_PAGE_SIZE + 1]; // +1 for address
|
||||
write_buffer[0] = start_addr + bytes_written;
|
||||
|
||||
for(uint8_t i = 0; i < bytes_to_write; i++) {
|
||||
write_buffer[i + 1] = buffer[bytes_written + i];
|
||||
}
|
||||
|
||||
furi_hal_i2c_acquire(&furi_hal_i2c_handle_external);
|
||||
|
||||
bool success = furi_hal_i2c_tx_ext(
|
||||
&furi_hal_i2c_handle_external,
|
||||
_i2c_addr_8bit,
|
||||
false,
|
||||
write_buffer,
|
||||
bytes_to_write + 1,
|
||||
FuriHalI2cBeginStart,
|
||||
FuriHalI2cEndStop,
|
||||
EEPROM_I2C_TIMEOUT);
|
||||
|
||||
furi_hal_i2c_release(&furi_hal_i2c_handle_external);
|
||||
|
||||
if(!success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait for write cycle to complete
|
||||
furi_delay_ms(10);
|
||||
|
||||
bytes_written += bytes_to_write;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EEPROM24C02::eraseAll() {
|
||||
// Fill entire memory with 0xFF
|
||||
uint8_t erase_buffer[EEPROM_24C02_PAGE_SIZE];
|
||||
for(uint8_t i = 0; i < EEPROM_24C02_PAGE_SIZE; i++) {
|
||||
erase_buffer[i] = 0xFF;
|
||||
}
|
||||
|
||||
// Erase page by page
|
||||
for(uint8_t page = 0; page < EEPROM_24C02_SIZE / EEPROM_24C02_PAGE_SIZE; page++) {
|
||||
uint8_t start_addr = page * EEPROM_24C02_PAGE_SIZE;
|
||||
if(!writeBytes(start_addr, erase_buffer, EEPROM_24C02_PAGE_SIZE)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EEPROM24C02::eraseRange(uint8_t start_addr, uint8_t length) {
|
||||
if(length == 0) return false;
|
||||
|
||||
// Check if range goes beyond memory
|
||||
uint16_t end_addr = (uint16_t)start_addr + length;
|
||||
if(end_addr > EEPROM_24C02_SIZE) {
|
||||
length = EEPROM_24C02_SIZE - start_addr;
|
||||
}
|
||||
|
||||
// Fill range with 0xFF
|
||||
uint8_t erase_buffer[EEPROM_24C02_PAGE_SIZE];
|
||||
for(uint8_t i = 0; i < EEPROM_24C02_PAGE_SIZE; i++) {
|
||||
erase_buffer[i] = 0xFF;
|
||||
}
|
||||
|
||||
return writeBytes(start_addr, erase_buffer, length);
|
||||
}
|
||||
|
||||
void EEPROM24C02::setAddress(uint8_t i2c_address_7bit) {
|
||||
_i2c_addr_8bit = i2c_address_7bit << 1;
|
||||
}
|
||||
|
||||
uint8_t EEPROM24C02::getAddress() {
|
||||
return _i2c_addr_8bit >> 1;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// 24C02 EEPROM I2C addresses (7-bit)
|
||||
// Standard addresses: 0x50-0x57 (A0-A2 pins)
|
||||
#define EEPROM_24C02_BASE_ADDR 0x50
|
||||
#define EEPROM_24C02_MAX_ADDR 0x57
|
||||
|
||||
// Memory size for 24C02
|
||||
#define EEPROM_24C02_SIZE 256 // 2KB = 2048 bits = 256 bytes
|
||||
#define EEPROM_24C02_PAGE_SIZE 8 // Page write size
|
||||
|
||||
// I2C operation timeout
|
||||
#define EEPROM_I2C_TIMEOUT 100
|
||||
|
||||
class EEPROM24C02 {
|
||||
private:
|
||||
uint8_t _i2c_addr_8bit;
|
||||
|
||||
public:
|
||||
EEPROM24C02(uint8_t i2c_address_7bit);
|
||||
|
||||
// Initialize communication with EEPROM
|
||||
bool init();
|
||||
|
||||
// Read single byte from address
|
||||
bool readByte(uint8_t memory_addr, uint8_t& data);
|
||||
|
||||
// Write single byte to address
|
||||
bool writeByte(uint8_t memory_addr, uint8_t data);
|
||||
|
||||
// Read multiple bytes (sequential read)
|
||||
bool readBytes(uint8_t start_addr, uint8_t* buffer, uint8_t length);
|
||||
|
||||
// Write multiple bytes (page write)
|
||||
bool writeBytes(uint8_t start_addr, const uint8_t* buffer, uint8_t length);
|
||||
|
||||
// Erase entire memory (fill with 0xFF)
|
||||
bool eraseAll();
|
||||
|
||||
// Erase range of bytes
|
||||
bool eraseRange(uint8_t start_addr, uint8_t length);
|
||||
|
||||
// Check if EEPROM is responding
|
||||
bool isAvailable();
|
||||
|
||||
// Set I2C address
|
||||
void setAddress(uint8_t i2c_address_7bit);
|
||||
|
||||
// Get current I2C address
|
||||
uint8_t getAddress();
|
||||
};
|
||||
@@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/canvas.h>
|
||||
|
||||
static const uint8_t image_DolphinMafia_0_bits[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x0e, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0xe0, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xfe, 0x7f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x55,
|
||||
0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0xaa, 0x00,
|
||||
0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x55, 0x55, 0x15, 0x00,
|
||||
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xaa, 0x2a, 0x00, 0x00, 0x04,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x55, 0x55, 0x55, 0x00, 0x08, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xaa, 0x2a, 0x00, 0x00, 0x08, 0xff, 0x3f,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x55, 0x55, 0x55, 0x00, 0xf8, 0x00, 0xc0, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xaa, 0x2a, 0x00, 0x00, 0x10, 0x00, 0x00, 0x01, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x55, 0x55, 0x55, 0x00, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xa0, 0xaa, 0x2a, 0x00, 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x60, 0xd5, 0xff, 0xff, 0x3f, 0x20, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xa0, 0xfa, 0xff, 0xff, 0xff, 0xff, 0x03, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x60, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xe0, 0xff, 0xff, 0xbf, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0,
|
||||
0xff, 0xff, 0x57, 0x55, 0x55, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff,
|
||||
0xff, 0xaa, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x5f,
|
||||
0x15, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xaf, 0x02,
|
||||
0xf8, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x55, 0x01, 0xff,
|
||||
0x1f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0x2a, 0xc0, 0x0f, 0xf8,
|
||||
0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, 0x5f, 0x15, 0xf0, 0x0f, 0xf8, 0x0f,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xf8, 0xff, 0xaf, 0x02, 0xf8, 0x0f, 0x1c, 0x0e, 0x1f,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xfc, 0xff, 0x57, 0x15, 0xf8, 0x1f, 0x1e, 0xfe, 0x60, 0x00,
|
||||
0x00, 0x00, 0xd6, 0x00, 0x50, 0xfe, 0xff, 0xab, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0x80, 0x00, 0x00,
|
||||
0x80, 0x01, 0xc8, 0x4e, 0xfe, 0xff, 0xf5, 0x17, 0xf0, 0xff, 0xcf, 0x00, 0x00, 0x01, 0x00, 0x40,
|
||||
0x00, 0x00, 0x20, 0xff, 0xff, 0xfb, 0x00, 0xe0, 0xff, 0x07, 0x00, 0x60, 0x01, 0x00, 0x20, 0x00,
|
||||
0x00, 0x18, 0xff, 0xff, 0x5d, 0x05, 0xc0, 0xff, 0x03, 0x00, 0x70, 0xe1, 0x07, 0xa0, 0xa3, 0xb0,
|
||||
0x06, 0xff, 0xff, 0xaa, 0x00, 0x80, 0xff, 0x01, 0x00, 0xfc, 0x01, 0x00, 0x60, 0x00, 0x00, 0x00,
|
||||
0xff, 0xff, 0x5d, 0x05, 0xc0, 0x7f, 0x00, 0x00, 0xb3, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xfe,
|
||||
0xff, 0xae, 0x00, 0x20, 0x60, 0x00, 0xc0, 0x80, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x9f,
|
||||
0x55, 0x05, 0x10, 0x40, 0x00, 0x30, 0x80, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x80, 0xaa,
|
||||
0x00, 0x10, 0x00, 0x00, 0x0c, 0x80, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x80, 0x55, 0x05,
|
||||
0x00, 0x00, 0x00, 0x03, 0x40, 0x00, 0x80, 0x10, 0x00, 0x00, 0x00, 0x00, 0x80, 0xaa, 0x00, 0x00,
|
||||
0x02, 0x80, 0x00, 0x20, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x05, 0x00, 0x02,
|
||||
0x60, 0x00, 0x10, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x0c, 0x18,
|
||||
0x00, 0x0c, 0x00, 0x20, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x15, 0x00, 0xf0, 0x07, 0x00,
|
||||
0x03, 0xc0, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00,
|
||||
0x20, 0x16, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x15, 0x00, 0x00, 0x00, 0x30, 0x00, 0x20,
|
||||
0x08, 0x21, 0x00, 0x00, 0x00, 0x00, 0x80, 0xab, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x20, 0x80,
|
||||
0x40, 0x00, 0x00, 0x00, 0x00, 0x40, 0x5d, 0x15, 0x00, 0x00, 0x00, 0x03, 0x00, 0x40, 0x80, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0xc0, 0xea, 0x02, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x40, 0x40, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x40, 0x55, 0x57, 0x00, 0x00, 0xf8, 0x07, 0x00, 0x80, 0x40, 0x00, 0x01, 0x00,
|
||||
0x00, 0x00, 0xc0, 0xaa, 0x3a, 0x00, 0x00, 0xfe, 0x05, 0x00, 0x80, 0x40, 0x00, 0x01, 0x00, 0x00,
|
||||
0x00, 0xe0, 0x55, 0xd5, 0x01, 0x00, 0xfc, 0x05, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||
0xf0, 0xab, 0x0a, 0x1e, 0x00, 0x70, 0x0c, 0x00, 0x80, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0xd8,
|
||||
0x57, 0x55, 0xe1, 0x01, 0x00, 0x14, 0x00, 0x80, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0xec, 0xaf,
|
||||
0x0a, 0x00, 0xfe, 0x07, 0x14, 0x00, 0xe0, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x7f, 0x55,
|
||||
0x05, 0x00, 0x18, 0x24, 0x00, 0x10, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0xeb, 0xff, 0x0a, 0x00,
|
||||
0x00, 0x20, 0x22, 0x00, 0x08, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0xf5, 0xff, 0x55, 0x05, 0x00,
|
||||
0x48, 0x22, 0x00, 0x04, 0x04, 0x10, 0x00, 0x00, 0x00, 0x80, 0xfa, 0xff, 0x0f, 0x00, 0x00, 0x8c,
|
||||
0x42, 0x00, 0x06, 0x08, 0x08, 0x00, 0x00, 0x00, 0x40, 0xf5, 0xff, 0x5f, 0x15, 0x00, 0x06, 0x4b,
|
||||
0x80, 0x09, 0x30, 0x04, 0x00, 0x00, 0x00, 0xc0, 0xfa, 0xff, 0xea, 0x00, 0x00, 0x07, 0x52, 0x40,
|
||||
0x10, 0xc0, 0x06, 0x00, 0x00, 0x00, 0x60, 0xf5, 0x7f, 0x55, 0x17, 0x80, 0x0f, 0x72, 0x30, 0x20,
|
||||
0x00, 0x07, 0x00, 0x00, 0x00, 0xb0, 0xfa, 0xbf, 0xaa, 0x38, 0xc0, 0x1f, 0xf4, 0xac, 0x42, 0x00,
|
||||
0x04, 0x00, 0x00, 0x00, 0x50, 0xf5, 0x5f, 0x55, 0xd5, 0xe1, 0x3f, 0x74, 0x57, 0x81, 0x01, 0x02,
|
||||
0x00, 0x00, 0x00, 0xa8, 0xfa, 0xaf, 0xaa, 0x80, 0xf3, 0x7f, 0xf8, 0xaa, 0x0a, 0x06, 0x01, 0x00,
|
||||
0x00, 0x00};
|
||||
|
||||
void drawScreen_1(Canvas* canvas) {
|
||||
canvas_set_bitmap_mode(canvas, true);
|
||||
|
||||
// Layer 3
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 86, 22, "24cXX ");
|
||||
|
||||
// Layer 3
|
||||
canvas_draw_str(canvas, 77, 10, "Dr.Mosfet ");
|
||||
|
||||
// Layer 4
|
||||
canvas_draw_str(canvas, 67, 34, "Programmer");
|
||||
|
||||
// DolphinMafia
|
||||
canvas_draw_xbm(canvas, -9, 12, 119, 62, image_DolphinMafia_0_bits);
|
||||
}
|
||||
|
After Width: | Height: | Size: 86 B |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,325 @@
|
||||
#include <stdio.h>
|
||||
#include <furi.h>
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
#include <notification/notification.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
static int matrix[6][7] = {0};
|
||||
static int cursorx = 3;
|
||||
static int cursory = 5;
|
||||
static int player = 1;
|
||||
static int scoreX = 0;
|
||||
static int scoreO = 0;
|
||||
|
||||
typedef struct {
|
||||
FuriMutex* mutex;
|
||||
} FourInRowState;
|
||||
|
||||
void init() {
|
||||
for(size_t i = 0; i < 6; i++) {
|
||||
for(size_t j = 0; j < 7; j++) {
|
||||
matrix[i][j] = 0;
|
||||
}
|
||||
}
|
||||
cursorx = 3;
|
||||
cursory = 5;
|
||||
player = 1;
|
||||
}
|
||||
|
||||
const NotificationSequence end = {
|
||||
&message_vibro_on,
|
||||
|
||||
&message_note_ds4,
|
||||
&message_delay_10,
|
||||
&message_sound_off,
|
||||
&message_delay_10,
|
||||
|
||||
&message_note_ds4,
|
||||
&message_delay_10,
|
||||
&message_sound_off,
|
||||
&message_delay_10,
|
||||
|
||||
&message_note_ds4,
|
||||
&message_delay_10,
|
||||
&message_sound_off,
|
||||
&message_delay_10,
|
||||
|
||||
&message_vibro_off,
|
||||
NULL,
|
||||
};
|
||||
|
||||
void intToStr(int num, char* str) {
|
||||
int i = 0, sign = 0;
|
||||
|
||||
if(num < 0) {
|
||||
num = -num;
|
||||
sign = 1;
|
||||
}
|
||||
|
||||
do {
|
||||
str[i++] = num % 10 + '0';
|
||||
num /= 10;
|
||||
} while(num > 0);
|
||||
|
||||
if(sign) {
|
||||
str[i++] = '-';
|
||||
}
|
||||
|
||||
str[i] = '\0';
|
||||
|
||||
// Reverse the string
|
||||
int j, len = i;
|
||||
char temp;
|
||||
for(j = 0; j < len / 2; j++) {
|
||||
temp = str[j];
|
||||
str[j] = str[len - j - 1];
|
||||
str[len - j - 1] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
int next_height(int x) {
|
||||
if(matrix[0][x] != 0) {
|
||||
return -1;
|
||||
}
|
||||
for(size_t y = 1; y < 6; y++) {
|
||||
if(matrix[y][x] != 0) {
|
||||
return y - 1;
|
||||
}
|
||||
}
|
||||
return 5;
|
||||
}
|
||||
|
||||
int wincheck() {
|
||||
for(size_t y = 0; y <= 2; y++) {
|
||||
for(size_t x = 0; x <= 6; x++) {
|
||||
if(matrix[y][x] != 0 && matrix[y][x] == matrix[y + 1][x] &&
|
||||
matrix[y][x] == matrix[y + 2][x] && matrix[y][x] == matrix[y + 3][x]) {
|
||||
return matrix[y][x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(size_t y = 0; y <= 5; y++) {
|
||||
for(size_t x = 0; x <= 3; x++) {
|
||||
if(matrix[y][x] != 0 && matrix[y][x] == matrix[y][x + 1] &&
|
||||
matrix[y][x] == matrix[y][x + 2] && matrix[y][x] == matrix[y][x + 3]) {
|
||||
return matrix[y][x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(size_t y = 0; y <= 2; y++) {
|
||||
for(size_t x = 0; x <= 3; x++) {
|
||||
if(matrix[y][x] != 0 && matrix[y][x] == matrix[y + 1][x + 1] &&
|
||||
matrix[y][x] == matrix[y + 2][x + 2] && matrix[y][x] == matrix[y + 3][x + 3]) {
|
||||
return matrix[y][x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(size_t y = 3; y <= 5; y++) {
|
||||
for(size_t x = 0; x <= 3; x++) {
|
||||
if(matrix[y][x] != 0 && matrix[y][x] == matrix[y - 1][x + 1] &&
|
||||
matrix[y][x] == matrix[y - 2][x + 2] && matrix[y][x] == matrix[y - 3][x + 3]) {
|
||||
return matrix[y][x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool tf = true;
|
||||
for(size_t y = 0; y < 6; y++) {
|
||||
for(size_t x = 0; x < 7; x++) {
|
||||
if(matrix[y][x] == 0) {
|
||||
tf = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(tf) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void draw_callback(Canvas* canvas, void* ctx) {
|
||||
furi_assert(ctx);
|
||||
const FourInRowState* fourinrow_state = ctx;
|
||||
|
||||
furi_mutex_acquire(fourinrow_state->mutex, FuriWaitForever);
|
||||
canvas_clear(canvas);
|
||||
|
||||
if(wincheck() != -1) {
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
|
||||
if(wincheck() == 0) {
|
||||
canvas_draw_str(canvas, 30, 35, "Draw! O_o");
|
||||
}
|
||||
if(wincheck() == 1) {
|
||||
canvas_draw_str(canvas, 30, 35, "Player X win!");
|
||||
}
|
||||
if(wincheck() == 2) {
|
||||
canvas_draw_str(canvas, 30, 35, "Player O win!");
|
||||
}
|
||||
|
||||
furi_mutex_release(fourinrow_state->mutex);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < 6; i++) {
|
||||
for(size_t j = 0; j < 7; j++) {
|
||||
char el[2];
|
||||
switch(matrix[i][j]) {
|
||||
case 0:
|
||||
strcpy(el, "_\0");
|
||||
break;
|
||||
|
||||
case 1:
|
||||
strcpy(el, "X\0");
|
||||
break;
|
||||
|
||||
case 2:
|
||||
strcpy(el, "O\0");
|
||||
break;
|
||||
}
|
||||
canvas_draw_str(canvas, j * 10 + 10, i * 10 + 10, el);
|
||||
}
|
||||
}
|
||||
canvas_draw_str(canvas, cursorx * 10 + 8, cursory * 10 + 10, "[ ]");
|
||||
|
||||
if(player == 1) {
|
||||
canvas_draw_str(canvas, 80, 10, "Turn: X");
|
||||
}
|
||||
if(player == 2) {
|
||||
canvas_draw_str(canvas, 80, 10, "Turn: O");
|
||||
}
|
||||
char scX[1];
|
||||
intToStr(scoreX, scX);
|
||||
char scO[1];
|
||||
intToStr(scoreO, scO);
|
||||
|
||||
canvas_draw_str(canvas, 80, 20, "X:");
|
||||
canvas_draw_str(canvas, 90, 20, scX);
|
||||
|
||||
canvas_draw_str(canvas, 80, 30, "O:");
|
||||
canvas_draw_str(canvas, 90, 30, scO);
|
||||
|
||||
furi_mutex_release(fourinrow_state->mutex);
|
||||
}
|
||||
|
||||
static void input_callback(InputEvent* input_event, void* ctx) {
|
||||
// Проверяем, что контекст не нулевой
|
||||
furi_assert(ctx);
|
||||
FuriMessageQueue* event_queue = ctx;
|
||||
|
||||
furi_message_queue_put(event_queue, input_event, FuriWaitForever);
|
||||
}
|
||||
|
||||
int32_t four_in_row_app(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
// Текущее событие типа InputEvent
|
||||
InputEvent event;
|
||||
// Очередь событий на 8 элементов размера InputEvent
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
||||
|
||||
FourInRowState* fourinrow_state = malloc(sizeof(FourInRowState));
|
||||
|
||||
fourinrow_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); // Alloc Mutex
|
||||
if(!fourinrow_state->mutex) {
|
||||
FURI_LOG_E("4inRow", "cannot create mutex\r\n");
|
||||
furi_message_queue_free(event_queue);
|
||||
free(fourinrow_state);
|
||||
return 255;
|
||||
}
|
||||
|
||||
dolphin_deed(DolphinDeedPluginGameStart);
|
||||
|
||||
// Создаем новый view port
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
// Создаем callback отрисовки, без контекста
|
||||
view_port_draw_callback_set(view_port, draw_callback, fourinrow_state);
|
||||
// Создаем callback нажатий на клавиши, в качестве контекста передаем
|
||||
// нашу очередь сообщений, чтоб запихивать в неё эти события
|
||||
view_port_input_callback_set(view_port, input_callback, event_queue);
|
||||
|
||||
// Создаем GUI приложения
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
// Подключаем view port к GUI в полноэкранном режиме
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
notification_message_block(notification, &sequence_display_backlight_enforce_on);
|
||||
|
||||
// Бесконечный цикл обработки очереди событий
|
||||
while(1) {
|
||||
// Выбираем событие из очереди в переменную event (ждем бесконечно долго, если очередь пуста)
|
||||
// и проверяем, что у нас получилось это сделать
|
||||
if(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) {
|
||||
if((event.type == InputTypePress) && (event.key == InputKeyBack)) {
|
||||
break;
|
||||
}
|
||||
|
||||
furi_mutex_acquire(fourinrow_state->mutex, FuriWaitForever);
|
||||
if(wincheck() != -1) {
|
||||
notification_message(notification, &end);
|
||||
furi_delay_ms(1000);
|
||||
if(wincheck() == 1) {
|
||||
scoreX++;
|
||||
}
|
||||
if(wincheck() == 2) {
|
||||
scoreO++;
|
||||
}
|
||||
init();
|
||||
furi_mutex_release(fourinrow_state->mutex);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(event.type == InputTypePress) {
|
||||
if(event.key == InputKeyOk) {
|
||||
int nh = next_height(cursorx);
|
||||
if(nh != -1) {
|
||||
matrix[nh][cursorx] = player;
|
||||
player = 3 - player;
|
||||
}
|
||||
}
|
||||
if(event.key == InputKeyUp) {
|
||||
//cursory--;
|
||||
}
|
||||
if(event.key == InputKeyDown) {
|
||||
//cursory++;
|
||||
}
|
||||
if(event.key == InputKeyLeft) {
|
||||
if(cursorx > 0) {
|
||||
cursorx--;
|
||||
}
|
||||
}
|
||||
if(event.key == InputKeyRight) {
|
||||
if(cursorx < 6) {
|
||||
cursorx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
furi_mutex_release(fourinrow_state->mutex);
|
||||
}
|
||||
view_port_update(view_port);
|
||||
}
|
||||
|
||||
// Чистим созданные объекты, связанные с интерфейсом
|
||||
view_port_enabled_set(view_port, false);
|
||||
gui_remove_view_port(gui, view_port);
|
||||
view_port_free(view_port);
|
||||
furi_message_queue_free(event_queue);
|
||||
furi_record_close(RECORD_GUI);
|
||||
// Clear notification
|
||||
notification_message_block(notification, &sequence_display_backlight_enforce_auto);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
|
||||
furi_mutex_free(fourinrow_state->mutex);
|
||||
|
||||
free(fourinrow_state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
After Width: | Height: | Size: 200 B |
@@ -0,0 +1 @@
|
||||
Four in row for flipper zero!!
|
||||
@@ -0,0 +1,17 @@
|
||||
App(
|
||||
appid="4inrow",
|
||||
name="4 in row",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="four_in_row_app",
|
||||
requires=[
|
||||
"gui",
|
||||
],
|
||||
stack_size=1 * 1024,
|
||||
order=90,
|
||||
fap_icon="4inrow_10px.png",
|
||||
fap_category="Games",
|
||||
fap_author="leo-need-more-coffee",
|
||||
fap_weburl="https://github.com/leo-need-more-coffee/flipperzero-4inrow",
|
||||
fap_version="1.3",
|
||||
fap_description="4 in row Game",
|
||||
)
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,6 @@
|
||||
# Flipper-DVD-Bounce
|
||||
**simple dvd-bounce application for flipper**
|
||||
|
||||
Y'know how dvd players got that thing that bounces around?
|
||||
|
||||
*This is that*
|
||||
@@ -0,0 +1,15 @@
|
||||
# qv. https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/AppManifests.md
|
||||
|
||||
App(
|
||||
appid="dvd_bounce",
|
||||
name="DVD Bouncer",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="bounce_moment",
|
||||
requires=[
|
||||
"gui",
|
||||
],
|
||||
stack_size=1 * 1024,
|
||||
fap_icon="iconimage.png",
|
||||
fap_category="Games",
|
||||
fap_icon_assets="assets",
|
||||
)
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
@@ -0,0 +1,142 @@
|
||||
#include <string.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
#include "dvd_bounce_icons.h"
|
||||
|
||||
//init some variables
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int mode = 0;
|
||||
bool bounce_up = false;
|
||||
bool bounce_right = true;
|
||||
char mode_str[12];
|
||||
|
||||
//the thing to draw to the screen
|
||||
static void app_draw_callback(Canvas* canvas, void* ctx) {
|
||||
UNUSED(ctx);
|
||||
canvas_clear(canvas);
|
||||
|
||||
//draws the ball to positions x and y
|
||||
canvas_draw_icon(canvas, x, y, &I_Ok_btn_pressed_13x13);
|
||||
|
||||
//displays the current mode
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, 2, 8, "Mode:");
|
||||
//converts mode int to string
|
||||
itoa(mode, mode_str, 10);
|
||||
canvas_draw_str(canvas, 28, 8, mode_str);
|
||||
switch(mode) {
|
||||
case 1:
|
||||
canvas_draw_str(canvas, 2, 16, "Left/Right");
|
||||
break;
|
||||
case 2:
|
||||
canvas_draw_str(canvas, 2, 16, "Up/Down");
|
||||
break;
|
||||
default:
|
||||
canvas_draw_str(canvas, 2, 16, "Normal");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void app_input_callback(InputEvent* input_event, void* ctx) {
|
||||
furi_assert(ctx);
|
||||
|
||||
FuriMessageQueue* event_queue = ctx;
|
||||
furi_message_queue_put(event_queue, input_event, FuriWaitForever);
|
||||
}
|
||||
|
||||
int32_t bounce_moment(void* p) {
|
||||
UNUSED(p);
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
||||
|
||||
// Configure viewport
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, app_draw_callback, view_port);
|
||||
view_port_input_callback_set(view_port, app_input_callback, event_queue);
|
||||
|
||||
// Register viewport in GUI
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
InputEvent event;
|
||||
|
||||
bool running = true;
|
||||
while(running) {
|
||||
if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) {
|
||||
if((event.type == InputTypePress) || (event.type == InputTypeRepeat)) {
|
||||
//arrows move the ball by 10 in their respective directions
|
||||
switch(event.key) {
|
||||
case InputKeyUp:
|
||||
y += -10;
|
||||
break;
|
||||
|
||||
case InputKeyDown:
|
||||
y += 10;
|
||||
break;
|
||||
|
||||
case InputKeyLeft:
|
||||
x += -10;
|
||||
break;
|
||||
|
||||
case InputKeyRight:
|
||||
x += 10;
|
||||
break;
|
||||
//sets the ball to the middle of the screen and sets the current mode
|
||||
case InputKeyOk:
|
||||
x = 51;
|
||||
y = 19;
|
||||
if(mode == 2) {
|
||||
mode = 0;
|
||||
} else {
|
||||
mode += 1;
|
||||
}
|
||||
break;
|
||||
//exits the program if back is pressed
|
||||
default:
|
||||
running = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//bunch of conditionals determining how the ball should move
|
||||
if(x <= 0) {
|
||||
bounce_up = false;
|
||||
}
|
||||
if(x >= 115) {
|
||||
bounce_up = true;
|
||||
}
|
||||
if(y <= 0) {
|
||||
bounce_right = true;
|
||||
}
|
||||
if(y >= 51) {
|
||||
bounce_right = false;
|
||||
}
|
||||
|
||||
if((bounce_up) && (mode != 2)) {
|
||||
x += -1;
|
||||
}
|
||||
if((!bounce_up) && (mode != 2)) {
|
||||
x += 1;
|
||||
}
|
||||
if((bounce_right) && (mode != 1)) {
|
||||
y += 1;
|
||||
}
|
||||
if((!bounce_right) && (mode != 1)) {
|
||||
y += -1;
|
||||
}
|
||||
|
||||
view_port_update(view_port);
|
||||
}
|
||||
//cleanup go brrrrr
|
||||
view_port_enabled_set(view_port, false);
|
||||
gui_remove_view_port(gui, view_port);
|
||||
view_port_free(view_port);
|
||||
furi_message_queue_free(event_queue);
|
||||
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
After Width: | Height: | Size: 170 B |
@@ -0,0 +1,52 @@
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Linker output
|
||||
*.ilk
|
||||
*.map
|
||||
*.exp
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
*.su
|
||||
*.idb
|
||||
*.pdb
|
||||
|
||||
# Kernel Module Compile Results
|
||||
*.mod*
|
||||
*.cmd
|
||||
.tmp_versions/
|
||||
modules.order
|
||||
Module.symvers
|
||||
Mkfile.old
|
||||
dkms.conf
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Struan Clark
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,58 @@
|
||||
# FlipBIP - BIP32/39/44
|
||||
|
||||
https://github.com/xtruan/FlipBIP
|
||||
|
||||
## Crypto toolkit for Flipper Zero
|
||||
- Using Trezor crypto libs from `core/v2.5.3` release
|
||||
|
||||
## Background
|
||||
|
||||
The goal of this project is to see how much crypto functionality can be brought to the Flipper Zero.
|
||||
|
||||
### Complete
|
||||
|
||||
- Trezor crypto C code ported into `crypto` subfolder
|
||||
- Adapted to use Flipper hardware RNG (see `crypto/rand.c`)
|
||||
- Imports and some C library functions modified for compatibility with FBT
|
||||
- Navigation and UI adapted from FAP Boilerplate app
|
||||
- BIP39 mnemonic generation
|
||||
- 24, 18, or 12 words configured in settings
|
||||
- BIP39 mnemonic to BIP39 seed generation
|
||||
- Hierarchical Deterministic (HD) wallet generation from seed
|
||||
- Generation of offline `m/44'/0'/0'/0` BTC wallet
|
||||
- Generation of offline `m/44'/60'/0'/0` ETH wallet (coded from the $SPORK Castle of ETHDenver 2023!)
|
||||
- Generation of offline `m/44'/3'/0'/0` DOGE wallet
|
||||
- Generation of offline `m/44'/133'/0'/0` ZEC transparent address wallet (by @wh00hw)
|
||||
- Similar features to: https://iancoleman.io/bip39/
|
||||
- Saving wallets to SD card
|
||||
- Wallets are saved to SD card upon creation in `apps_data/flipbip`
|
||||
- NOTE: `apps_data` folder must already exist on SD card!
|
||||
- Saved wallets can be viewed between app runs
|
||||
- Wallets are encrypted with a randomly generated key, and that key is also encrypted
|
||||
- `.flipbip.dat` and `.flipbip.key` files are both required to be in `apps_data/flipbip`
|
||||
- Backups of both these files `.flipbip.dat.bak` and `.flipbip.key.bak` are also maintained
|
||||
- If you want to externally back up your wallet, I recommend copying all these files, and storing the `key` and `dat` files seperately
|
||||
- NOTE: The wallets should be decently tough to crack off of a Flipper, however any Flipper with the app installed can load a wallet in the `apps_data/flipbip` directory if both the `key` and `dat` file are present
|
||||
- BIP39 passphrase support
|
||||
- Configured in settings, not persisted between runs for security
|
||||
- Import your own mnemonic
|
||||
- Lots of typing required but you can now use the wallet with an existing mnemonic you have saved
|
||||
- Useful to convert paper backup to keys and receive addresses without relying on a laptop or phone
|
||||
- Improved receive address generation features
|
||||
- Addresses are now generated at the same time as other pieces of wallet info
|
||||
- This slows down initial wallet load, but makes UI much more responsive
|
||||
- QR code files are now generated for each address and stored in the `apps_data/flipbip` directory
|
||||
- This app is required to view the QR code files: https://github.com/bmatcuk/flipperzero-qrcode
|
||||
- NOTE: This happens during the `View Wallet` step; you must view a wallet after generating/importing a wallet in order to ensure the address QR files are correct
|
||||
- Broke out crypto functionality into its own library using `fap_private_libs` feature
|
||||
|
||||
### Work in Progress
|
||||
|
||||
- More coin types
|
||||
- Support for more custom BIP32 wallet paths
|
||||
|
||||
### (FAR) Future
|
||||
|
||||
- Custom wallet security
|
||||
- User specified password
|
||||
- USB/Bluetooth wallet functionality
|
||||
@@ -0,0 +1,22 @@
|
||||
App(
|
||||
appid="flipbip",
|
||||
name="FlipBIP Crypto Wallet",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="flipbip_app",
|
||||
requires=[
|
||||
"gui",
|
||||
],
|
||||
stack_size=3 * 1024,
|
||||
order=10,
|
||||
fap_icon="flipbip_10px.png",
|
||||
fap_private_libs=[
|
||||
Lib(
|
||||
name="crypto",
|
||||
),
|
||||
],
|
||||
fap_category="Tools",
|
||||
fap_author="Struan Clark (xtruan)",
|
||||
fap_weburl="https://github.com/xtruan/FlipBIP",
|
||||
fap_version=(1, 17),
|
||||
fap_description="Crypto wallet for Flipper",
|
||||
)
|
||||
@@ -0,0 +1,239 @@
|
||||
#include "flipbip.h"
|
||||
#include "helpers/flipbip_file.h"
|
||||
// From: lib/crypto
|
||||
#include <memzero.h>
|
||||
#include <bip39.h>
|
||||
|
||||
#define MNEMONIC_MENU_DEFAULT "Import mnemonic seed"
|
||||
#define MNEMONIC_MENU_SUCCESS "Import seed (success)"
|
||||
#define MNEMONIC_MENU_FAILURE "Import seed (failed!)"
|
||||
|
||||
bool flipbip_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
FlipBip* app = context;
|
||||
return scene_manager_handle_custom_event(app->scene_manager, event);
|
||||
}
|
||||
|
||||
void flipbip_tick_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
FlipBip* app = context;
|
||||
scene_manager_handle_tick_event(app->scene_manager);
|
||||
}
|
||||
|
||||
//leave app if back button pressed
|
||||
bool flipbip_navigation_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
FlipBip* app = context;
|
||||
return scene_manager_handle_back_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static void text_input_callback(void* context) {
|
||||
furi_assert(context);
|
||||
FlipBip* app = context;
|
||||
bool handled = false;
|
||||
|
||||
// check that there is text in the input
|
||||
if(strlen(app->input_text) > 0) {
|
||||
if(app->input_state == FlipBipTextInputPassphrase) {
|
||||
if(app->passphrase == FlipBipPassphraseOn) {
|
||||
strcpy(app->passphrase_text, app->input_text);
|
||||
}
|
||||
// clear input text
|
||||
memzero(app->input_text, TEXT_BUFFER_SIZE);
|
||||
// reset input state
|
||||
app->input_state = FlipBipTextInputDefault;
|
||||
handled = true;
|
||||
// switch back to settings view
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdSettings);
|
||||
} else if(app->input_state == FlipBipTextInputMnemonic) {
|
||||
if(app->import_from_mnemonic == 1) {
|
||||
strcpy(app->import_mnemonic_text, app->input_text);
|
||||
|
||||
int status = FlipBipStatusSuccess;
|
||||
// Check if the mnemonic is valid
|
||||
if(mnemonic_check(app->import_mnemonic_text) == 0)
|
||||
status = FlipBipStatusMnemonicCheckError; // 13 = mnemonic check error
|
||||
// Save the mnemonic to persistent storage
|
||||
else if(!flipbip_save_file_secure(app->import_mnemonic_text))
|
||||
status = FlipBipStatusSaveError; // 12 = save error
|
||||
|
||||
if(status == FlipBipStatusSuccess) {
|
||||
app->mnemonic_menu_text = MNEMONIC_MENU_SUCCESS;
|
||||
//notification_message(app->notification, &sequence_blink_cyan_100);
|
||||
//flipbip_play_happy_bump(app);
|
||||
} else {
|
||||
app->mnemonic_menu_text = MNEMONIC_MENU_FAILURE;
|
||||
//notification_message(app->notification, &sequence_blink_red_100);
|
||||
//flipbip_play_long_bump(app);
|
||||
}
|
||||
|
||||
memzero(app->import_mnemonic_text, TEXT_BUFFER_SIZE);
|
||||
}
|
||||
// clear input text
|
||||
memzero(app->input_text, TEXT_BUFFER_SIZE);
|
||||
// reset input state
|
||||
app->input_state = FlipBipTextInputDefault;
|
||||
handled = true;
|
||||
// exit scene 1 instance that's being used for text input and go back to menu
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
//view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdMenu);
|
||||
}
|
||||
}
|
||||
|
||||
if(!handled) {
|
||||
// clear input text
|
||||
memzero(app->input_text, TEXT_BUFFER_SIZE);
|
||||
// reset input state
|
||||
app->input_state = FlipBipTextInputDefault;
|
||||
// something went wrong, switch to menu view
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdMenu);
|
||||
}
|
||||
}
|
||||
|
||||
static void flipbip_scene_renew_dialog_callback(DialogExResult result, void* context) {
|
||||
FlipBip* app = context;
|
||||
if(result == DialogExResultRight) {
|
||||
app->wallet_create(app);
|
||||
} else {
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdMenu);
|
||||
}
|
||||
}
|
||||
|
||||
static void flipbip_wallet_create(void* context) {
|
||||
FlipBip* app = context;
|
||||
furi_assert(app);
|
||||
scene_manager_set_scene_state(app->scene_manager, FlipBipSceneMenu, SubmenuIndexScene1New);
|
||||
scene_manager_next_scene(app->scene_manager, FlipBipSceneScene_1);
|
||||
}
|
||||
|
||||
FlipBip* flipbip_app_alloc() {
|
||||
FlipBip* app = malloc(sizeof(FlipBip));
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
//app->notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
// Turn backlight on, believe me this makes testing your app easier
|
||||
//notification_message(app->notification, &sequence_display_backlight_on);
|
||||
|
||||
// Scene additions
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
|
||||
|
||||
app->scene_manager = scene_manager_alloc(&flipbip_scene_handlers, app);
|
||||
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
app->view_dispatcher, flipbip_navigation_event_callback);
|
||||
view_dispatcher_set_tick_event_callback(
|
||||
app->view_dispatcher, flipbip_tick_event_callback, 100);
|
||||
view_dispatcher_set_custom_event_callback(app->view_dispatcher, flipbip_custom_event_callback);
|
||||
app->submenu = submenu_alloc();
|
||||
|
||||
// Settings
|
||||
app->bip39_strength = FlipBipStrength256; // 256 bits (24 words)
|
||||
app->passphrase = FlipBipPassphraseOff;
|
||||
|
||||
// Main menu
|
||||
app->bip44_coin = FlipBipCoinBTC0; // 0 (BTC)
|
||||
app->overwrite_saved_seed = 0;
|
||||
app->import_from_mnemonic = 0;
|
||||
app->mnemonic_menu_text = MNEMONIC_MENU_DEFAULT;
|
||||
|
||||
// Text input
|
||||
app->input_state = FlipBipTextInputDefault;
|
||||
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, FlipBipViewIdMenu, submenu_get_view(app->submenu));
|
||||
app->flipbip_scene_1 = flipbip_scene_1_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, FlipBipViewIdScene1, flipbip_scene_1_get_view(app->flipbip_scene_1));
|
||||
app->variable_item_list = variable_item_list_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
FlipBipViewIdSettings,
|
||||
variable_item_list_get_view(app->variable_item_list));
|
||||
|
||||
app->text_input = text_input_alloc();
|
||||
text_input_set_result_callback(
|
||||
app->text_input,
|
||||
text_input_callback,
|
||||
(void*)app,
|
||||
app->input_text,
|
||||
TEXT_BUFFER_SIZE,
|
||||
// clear default text
|
||||
true);
|
||||
//text_input_set_header_text(app->text_input, "Input");
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, FlipBipViewIdTextInput, text_input_get_view(app->text_input));
|
||||
|
||||
app->wallet_create = flipbip_wallet_create;
|
||||
app->renew_dialog = dialog_ex_alloc();
|
||||
dialog_ex_set_result_callback(app->renew_dialog, flipbip_scene_renew_dialog_callback);
|
||||
dialog_ex_set_context(app->renew_dialog, app);
|
||||
dialog_ex_set_left_button_text(app->renew_dialog, "No");
|
||||
dialog_ex_set_right_button_text(app->renew_dialog, "Yes");
|
||||
dialog_ex_set_header(
|
||||
app->renew_dialog,
|
||||
"Current wallet\nwill be deleted!\nProceed?",
|
||||
16,
|
||||
12,
|
||||
AlignLeft,
|
||||
AlignTop);
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, FlipBipViewRenewConfirm, dialog_ex_get_view(app->renew_dialog));
|
||||
|
||||
// End Scene Additions
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
void flipbip_app_free(FlipBip* app) {
|
||||
furi_assert(app);
|
||||
|
||||
// Scene manager
|
||||
scene_manager_free(app->scene_manager);
|
||||
|
||||
text_input_free(app->text_input);
|
||||
|
||||
// View Dispatcher
|
||||
view_dispatcher_remove_view(app->view_dispatcher, FlipBipViewIdMenu);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, FlipBipViewIdScene1);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, FlipBipViewIdSettings);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, FlipBipViewIdTextInput);
|
||||
submenu_free(app->submenu);
|
||||
|
||||
view_dispatcher_remove_view(app->view_dispatcher, FlipBipViewRenewConfirm);
|
||||
dialog_ex_free(app->renew_dialog);
|
||||
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
app->gui = NULL;
|
||||
//app->notification = NULL;
|
||||
|
||||
//Remove whatever is left
|
||||
memzero(app, sizeof(FlipBip));
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t flipbip_app(void* p) {
|
||||
UNUSED(p);
|
||||
FlipBip* app = flipbip_app_alloc();
|
||||
|
||||
// Disabled because causes exit on custom firmwares such as RM
|
||||
/*if(!furi_hal_region_is_provisioned()) {
|
||||
flipbip_app_free(app);
|
||||
return 1;
|
||||
}*/
|
||||
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
scene_manager_next_scene(app->scene_manager, FlipBipSceneMenu); //Start with menu
|
||||
|
||||
furi_hal_power_suppress_charge_enter();
|
||||
|
||||
view_dispatcher_run(app->view_dispatcher);
|
||||
|
||||
furi_hal_power_suppress_charge_exit();
|
||||
flipbip_app_free(app);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
#include <stdlib.h>
|
||||
//#include <notification/notification_messages.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <gui/modules/dialog_ex.h>
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
#include <gui/modules/text_input.h>
|
||||
#include "scenes/flipbip_scene.h"
|
||||
#include "views/flipbip_scene_1.h"
|
||||
|
||||
#define FLIPBIP_VERSION "v1.17"
|
||||
|
||||
#define COIN_BTC 0
|
||||
#define COIN_DOGE 3
|
||||
#define COIN_ETH 60
|
||||
#define COIN_ZEC 133
|
||||
|
||||
#define TEXT_BUFFER_SIZE 256
|
||||
|
||||
typedef struct {
|
||||
Gui* gui;
|
||||
// NotificationApp* notification;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
Submenu* submenu;
|
||||
SceneManager* scene_manager;
|
||||
VariableItemList* variable_item_list;
|
||||
TextInput* text_input;
|
||||
DialogEx* renew_dialog;
|
||||
FlipBipScene1* flipbip_scene_1;
|
||||
char* mnemonic_menu_text;
|
||||
// Settings options
|
||||
int bip39_strength;
|
||||
int passphrase;
|
||||
// Main menu options
|
||||
int bip44_coin;
|
||||
int overwrite_saved_seed;
|
||||
int import_from_mnemonic;
|
||||
// Text input
|
||||
int input_state;
|
||||
char passphrase_text[TEXT_BUFFER_SIZE];
|
||||
char import_mnemonic_text[TEXT_BUFFER_SIZE];
|
||||
char input_text[TEXT_BUFFER_SIZE];
|
||||
|
||||
void (*wallet_create)(void* context);
|
||||
} FlipBip;
|
||||
|
||||
typedef enum {
|
||||
FlipBipViewIdStartscreen,
|
||||
FlipBipViewIdMenu,
|
||||
FlipBipViewIdScene1,
|
||||
FlipBipViewIdSettings,
|
||||
FlipBipViewIdTextInput,
|
||||
FlipBipViewRenewConfirm,
|
||||
} FlipBipViewId;
|
||||
|
||||
typedef enum {
|
||||
FlipBipStrength128,
|
||||
FlipBipStrength192,
|
||||
FlipBipStrength256,
|
||||
} FlipBipStrengthState;
|
||||
|
||||
typedef enum {
|
||||
FlipBipPassphraseOff,
|
||||
FlipBipPassphraseOn,
|
||||
} FlipBipPassphraseState;
|
||||
|
||||
typedef enum {
|
||||
FlipBipCoinBTC0,
|
||||
FlipBipCoinETH60,
|
||||
FlipBipCoinDOGE3,
|
||||
FlipBipCoinZEC133,
|
||||
} FlipBipCoin;
|
||||
|
||||
typedef enum {
|
||||
FlipBipTextInputDefault,
|
||||
FlipBipTextInputPassphrase,
|
||||
FlipBipTextInputMnemonic
|
||||
} FlipBipTextInputState;
|
||||
|
||||
typedef enum {
|
||||
FlipBipStatusSuccess = 0,
|
||||
FlipBipStatusReturn = 10,
|
||||
FlipBipStatusLoadError = 11,
|
||||
FlipBipStatusSaveError = 12,
|
||||
FlipBipStatusMnemonicCheckError = 13,
|
||||
} FlipBipStatus;
|
||||
|
||||
typedef enum {
|
||||
SubmenuIndexScene1BTC = 10,
|
||||
SubmenuIndexScene1ETH,
|
||||
SubmenuIndexScene1DOGE,
|
||||
SubmenuIndexScene1ZEC,
|
||||
SubmenuIndexScene1New,
|
||||
SubmenuIndexScene1Renew,
|
||||
SubmenuIndexScene1Import,
|
||||
SubmenuIndexSettings,
|
||||
SubmenuIndexNOP,
|
||||
} SubmenuIndex;
|
||||
|
After Width: | Height: | Size: 183 B |
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
FlipBipCustomEventScene1Up,
|
||||
FlipBipCustomEventScene1Down,
|
||||
FlipBipCustomEventScene1Left,
|
||||
FlipBipCustomEventScene1Right,
|
||||
FlipBipCustomEventScene1Ok,
|
||||
FlipBipCustomEventScene1Back,
|
||||
} FlipBipCustomEvent;
|
||||
@@ -0,0 +1,314 @@
|
||||
#include "flipbip_file.h"
|
||||
#include <storage/storage.h>
|
||||
#include <loader/loader.h>
|
||||
#include "../helpers/flipbip_string.h"
|
||||
// From: lib/crypto
|
||||
#include <memzero.h>
|
||||
#include <rand.h>
|
||||
|
||||
// #define FLIPBIP_APP_BASE_FOLDER APP_DATA_PATH("flipbip")
|
||||
#define FLIPBIP_APP_BASE_FOLDER EXT_PATH("apps_data/flipbip")
|
||||
#define FLIPBIP_APP_BASE_FOLDER_PATH(path) FLIPBIP_APP_BASE_FOLDER "/" path
|
||||
#define FLIPBIP_DAT_FILE_NAME ".flipbip.dat"
|
||||
// #define FLIPBIP_DAT_FILE_NAME ".flipbip.dat.txt"
|
||||
#define FLIPBIP_DAT_FILE_NAME_BAK ".flipbip.dat.bak"
|
||||
#define FLIPBIP_KEY_FILE_NAME ".flipbip.key"
|
||||
// #define FLIPBIP_KEY_FILE_NAME ".flipbip.key.txt"
|
||||
#define FLIPBIP_KEY_FILE_NAME_BAK ".flipbip.key.bak"
|
||||
#define FLIPBIP_DAT_PATH FLIPBIP_APP_BASE_FOLDER_PATH(FLIPBIP_DAT_FILE_NAME)
|
||||
#define FLIPBIP_DAT_PATH_BAK FLIPBIP_APP_BASE_FOLDER_PATH(FLIPBIP_DAT_FILE_NAME_BAK)
|
||||
#define FLIPBIP_KEY_PATH FLIPBIP_APP_BASE_FOLDER_PATH(FLIPBIP_KEY_FILE_NAME)
|
||||
#define FLIPBIP_KEY_PATH_BAK FLIPBIP_APP_BASE_FOLDER_PATH(FLIPBIP_KEY_FILE_NAME_BAK)
|
||||
|
||||
const char* TEXT_QRFILE = "Filetype: QRCode\n"
|
||||
"Version: 0\n"
|
||||
"Message: "; // 37 chars + 1 null
|
||||
#define FILE_HLEN 4
|
||||
#define FILE_KLEN 256
|
||||
#define FILE_SLEN 512
|
||||
#define FILE_MAX_PATH_LEN 48
|
||||
#define FILE_MAX_QRFILE_CONTENT 90
|
||||
const char* FILE_HSTR = "fb01";
|
||||
const char* FILE_K1 = "fb0131d5cf688221c109163908ebe51debb46227c6cc8b37641910833222772a"
|
||||
"baefe6d9ceb651842260e0d1e05e3b90d15e7d5ffaaabc0207bf200a117793a2";
|
||||
|
||||
bool flipbip_load_file(
|
||||
char* settings,
|
||||
size_t slen,
|
||||
const FlipBipFile file_type,
|
||||
const char* file_name) {
|
||||
bool ret = false;
|
||||
const char* path;
|
||||
char path_buf[FILE_MAX_PATH_LEN] = {0};
|
||||
if(file_type == FlipBipFileKey) {
|
||||
path = FLIPBIP_KEY_PATH;
|
||||
} else if(file_type == FlipBipFileDat) {
|
||||
path = FLIPBIP_DAT_PATH;
|
||||
} else {
|
||||
strcpy(path_buf, FLIPBIP_APP_BASE_FOLDER); // 22
|
||||
strcpy(path_buf + strlen(path_buf), "/");
|
||||
strcpy(path_buf + strlen(path_buf), file_name);
|
||||
path = path_buf;
|
||||
}
|
||||
|
||||
Storage* fs_api = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
File* settings_file = storage_file_alloc(fs_api);
|
||||
if(storage_file_open(settings_file, path, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||
char chr;
|
||||
size_t i = 0;
|
||||
while((storage_file_read(settings_file, &chr, 1) == 1) &&
|
||||
!storage_file_eof(settings_file) && !isspace(chr)) {
|
||||
if(i < slen) {
|
||||
settings[i] = chr;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
ret = true;
|
||||
} else {
|
||||
memzero(settings, strlen(settings));
|
||||
settings[0] = '\0';
|
||||
ret = false;
|
||||
}
|
||||
storage_file_close(settings_file);
|
||||
storage_file_free(settings_file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
if(strlen(settings) > 0) {
|
||||
Storage* fs_api = furi_record_open(RECORD_STORAGE);
|
||||
FileInfo layout_file_info;
|
||||
FS_Error file_check_err = storage_common_stat(fs_api, path, &layout_file_info);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
if(file_check_err != FSE_OK) {
|
||||
memzero(settings, strlen(settings));
|
||||
settings[0] = '\0';
|
||||
ret = false;
|
||||
}
|
||||
// if(layout_file_info.size != 256) {
|
||||
// memzero(settings, strlen(settings));
|
||||
// settings[0] = '\0';
|
||||
// }
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool flipbip_has_file(const FlipBipFile file_type, const char* file_name, const bool remove) {
|
||||
bool ret = false;
|
||||
const char* path;
|
||||
char path_buf[FILE_MAX_PATH_LEN] = {0};
|
||||
if(file_type == FlipBipFileKey) {
|
||||
path = FLIPBIP_KEY_PATH;
|
||||
} else if(file_type == FlipBipFileDat) {
|
||||
path = FLIPBIP_DAT_PATH;
|
||||
} else {
|
||||
strcpy(path_buf, FLIPBIP_APP_BASE_FOLDER); // 22
|
||||
strcpy(path_buf + strlen(path_buf), "/");
|
||||
strcpy(path_buf + strlen(path_buf), file_name);
|
||||
path = path_buf;
|
||||
}
|
||||
|
||||
Storage* fs_api = furi_record_open(RECORD_STORAGE);
|
||||
if(remove) {
|
||||
ret = storage_simply_remove(fs_api, path);
|
||||
} else {
|
||||
ret = storage_file_exists(fs_api, path);
|
||||
}
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool flipbip_save_file(
|
||||
const char* settings,
|
||||
const FlipBipFile file_type,
|
||||
const char* file_name,
|
||||
const bool append) {
|
||||
bool ret = false;
|
||||
const char* path;
|
||||
const char* path_bak;
|
||||
char path_buf[FILE_MAX_PATH_LEN] = {0};
|
||||
if(file_type == FlipBipFileKey) {
|
||||
path = FLIPBIP_KEY_PATH;
|
||||
path_bak = FLIPBIP_KEY_PATH_BAK;
|
||||
} else if(file_type == FlipBipFileDat) {
|
||||
path = FLIPBIP_DAT_PATH;
|
||||
path_bak = FLIPBIP_DAT_PATH_BAK;
|
||||
} else {
|
||||
strcpy(path_buf, FLIPBIP_APP_BASE_FOLDER); // 22
|
||||
strcpy(path_buf + strlen(path_buf), "/");
|
||||
strcpy(path_buf + strlen(path_buf), file_name);
|
||||
path = path_buf;
|
||||
path_bak = NULL;
|
||||
}
|
||||
int open_mode = FSOM_OPEN_ALWAYS;
|
||||
if(append) {
|
||||
open_mode = FSOM_OPEN_APPEND;
|
||||
}
|
||||
|
||||
Storage* fs_api = furi_record_open(RECORD_STORAGE);
|
||||
// // if the key file exists, we don't want to overwrite it
|
||||
// if (key_file && storage_file_exists(fs_api, path)) {
|
||||
// furi_record_close(RECORD_STORAGE);
|
||||
// ret = true;
|
||||
// return ret;
|
||||
// }
|
||||
// try to create the folder
|
||||
storage_simply_mkdir(fs_api, FLIPBIP_APP_BASE_FOLDER);
|
||||
|
||||
File* settings_file = storage_file_alloc(fs_api);
|
||||
if(storage_file_open(settings_file, path, FSAM_WRITE, open_mode)) {
|
||||
storage_file_write(settings_file, settings, strlen(settings));
|
||||
storage_file_write(settings_file, "\n", 1);
|
||||
ret = true;
|
||||
}
|
||||
storage_file_close(settings_file);
|
||||
storage_file_free(settings_file);
|
||||
|
||||
if(path_bak != NULL) {
|
||||
File* settings_file_bak = storage_file_alloc(fs_api);
|
||||
if(storage_file_open(settings_file_bak, path_bak, FSAM_WRITE, open_mode)) {
|
||||
storage_file_write(settings_file_bak, settings, strlen(settings));
|
||||
storage_file_write(settings_file_bak, "\n", 1);
|
||||
}
|
||||
storage_file_close(settings_file_bak);
|
||||
storage_file_free(settings_file_bak);
|
||||
}
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool flipbip_save_qrfile(
|
||||
const char* qr_msg_prefix,
|
||||
const char* qr_msg_content,
|
||||
const char* file_name) {
|
||||
char qr_buf[FILE_MAX_QRFILE_CONTENT] = {0};
|
||||
strcpy(qr_buf, TEXT_QRFILE);
|
||||
strcpy(qr_buf + strlen(qr_buf), qr_msg_prefix);
|
||||
strcpy(qr_buf + strlen(qr_buf), qr_msg_content);
|
||||
return flipbip_save_file(qr_buf, FlipBipFileOther, file_name, false);
|
||||
}
|
||||
|
||||
bool flipbip_load_file_secure(char* settings) {
|
||||
const size_t dlen = FILE_HLEN + FILE_SLEN + 1;
|
||||
|
||||
// allocate memory for key/data
|
||||
char* data = malloc(dlen);
|
||||
memzero(data, dlen);
|
||||
|
||||
// load k2 from file
|
||||
if(!flipbip_load_file(data, dlen, FlipBipFileKey, NULL)) return false;
|
||||
|
||||
// check header
|
||||
if(data[0] != FILE_HSTR[0] || data[1] != FILE_HSTR[1] || data[2] != FILE_HSTR[2] ||
|
||||
data[3] != FILE_HSTR[3]) {
|
||||
memzero(data, dlen);
|
||||
free(data);
|
||||
return false;
|
||||
}
|
||||
// seek --> header
|
||||
data += FILE_HLEN;
|
||||
|
||||
// prepare k1
|
||||
uint8_t k1[64];
|
||||
flipbip_xtob(FILE_K1, k1, strlen(FILE_K1) / 2);
|
||||
|
||||
// load k2 from file buffer (secured by k1)
|
||||
flipbip_cipher(k1, strlen(FILE_K1) / 2, data, data, FILE_KLEN);
|
||||
uint8_t k2[128];
|
||||
flipbip_xtob(data, k2, FILE_KLEN / 2);
|
||||
// zero k2 buffer
|
||||
memzero(data, FILE_KLEN);
|
||||
// seek <-- header
|
||||
data -= FILE_HLEN;
|
||||
|
||||
// load data from file
|
||||
if(!flipbip_load_file(data, dlen, FlipBipFileDat, NULL)) return false;
|
||||
|
||||
// check header
|
||||
if(data[0] != FILE_HSTR[0] || data[1] != FILE_HSTR[1] || data[2] != FILE_HSTR[2] ||
|
||||
data[3] != FILE_HSTR[3]) {
|
||||
memzero(data, dlen);
|
||||
free(data);
|
||||
memzero(k1, strlen(FILE_K1) / 2);
|
||||
memzero(k2, FILE_KLEN / 2);
|
||||
return false;
|
||||
}
|
||||
// seek --> header
|
||||
data += FILE_HLEN;
|
||||
|
||||
// load settings from file buffer (secured by k2)
|
||||
flipbip_cipher(k2, FILE_KLEN / 2, data, data, FILE_SLEN);
|
||||
flipbip_xtob(data, (unsigned char*)data, FILE_SLEN / 2);
|
||||
|
||||
// copy to output
|
||||
strcpy(settings, data);
|
||||
|
||||
// seek <-- header
|
||||
data -= FILE_HLEN;
|
||||
|
||||
// clear memory
|
||||
memzero(data, dlen);
|
||||
free(data);
|
||||
memzero(k1, strlen(FILE_K1) / 2);
|
||||
memzero(k2, FILE_KLEN / 2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool flipbip_save_file_secure(const char* settings) {
|
||||
const size_t dlen = FILE_HLEN + FILE_SLEN + 1;
|
||||
|
||||
// cap settings to 256 bytes
|
||||
size_t len = strlen(settings);
|
||||
if(len > (FILE_SLEN / 2)) len = FILE_SLEN / 2;
|
||||
|
||||
// allocate memory for key/data
|
||||
char* data = malloc(dlen);
|
||||
memzero(data, dlen);
|
||||
|
||||
// write header
|
||||
strncpy(data, FILE_HSTR, FILE_HLEN);
|
||||
// seek --> header
|
||||
data += FILE_HLEN;
|
||||
|
||||
// prepare k1
|
||||
uint8_t k1[64];
|
||||
flipbip_xtob(FILE_K1, k1, strlen(FILE_K1) / 2);
|
||||
|
||||
// generate k2
|
||||
uint8_t k2[128];
|
||||
random_buffer(k2, FILE_KLEN / 2);
|
||||
|
||||
// write k2 to file buffer (secured by k1)
|
||||
flipbip_btox(k2, FILE_KLEN / 2, data);
|
||||
flipbip_cipher(k1, strlen(FILE_K1) / 2, data, data, FILE_KLEN);
|
||||
|
||||
// seek <-- header
|
||||
data -= FILE_HLEN;
|
||||
// save k2 to file
|
||||
flipbip_save_file(data, FlipBipFileKey, NULL, false);
|
||||
// seek --> header
|
||||
data += FILE_HLEN;
|
||||
// zero k2 memory
|
||||
memzero(data, FILE_KLEN);
|
||||
|
||||
// write settings to file buffer (secured by k2)
|
||||
flipbip_btox((uint8_t*)settings, len, data);
|
||||
flipbip_cipher(k2, FILE_KLEN / 2, data, data, FILE_SLEN);
|
||||
|
||||
// seek <-- header
|
||||
data -= FILE_HLEN;
|
||||
// save data to file
|
||||
flipbip_save_file(data, FlipBipFileDat, NULL, false);
|
||||
|
||||
// clear memory
|
||||
memzero(data, dlen);
|
||||
free(data);
|
||||
memzero(k1, strlen(FILE_K1) / 2);
|
||||
memzero(k2, FILE_KLEN / 2);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef enum {
|
||||
FlipBipFileDat,
|
||||
FlipBipFileKey,
|
||||
FlipBipFileOther,
|
||||
} FlipBipFile;
|
||||
|
||||
bool flipbip_has_file(const FlipBipFile file_type, const char* file_name, const bool remove);
|
||||
bool flipbip_load_file(
|
||||
char* settings,
|
||||
size_t slen,
|
||||
const FlipBipFile file_type,
|
||||
const char* file_name);
|
||||
bool flipbip_save_file(
|
||||
const char* settings,
|
||||
const FlipBipFile file_type,
|
||||
const char* file_name,
|
||||
const bool append);
|
||||
|
||||
bool flipbip_save_qrfile(
|
||||
const char* qr_msg_prefix,
|
||||
const char* qr_msg_content,
|
||||
const char* file_name);
|
||||
|
||||
bool flipbip_load_file_secure(char* settings);
|
||||
bool flipbip_save_file_secure(const char* settings);
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (c) 1988 Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
#include "flipbip_string.h"
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
// From: lib/crypto
|
||||
#include <memzero.h>
|
||||
#include <rc4.h>
|
||||
|
||||
char* flipbip_strtok(char* s, const char* delim) {
|
||||
static char* last;
|
||||
return flipbip_strtok_r(s, delim, &last);
|
||||
}
|
||||
char* flipbip_strtok_r(char* s, const char* delim, char** last) {
|
||||
char* spanp;
|
||||
int c, sc;
|
||||
char* tok;
|
||||
if(s == NULL && (s = *last) == NULL) return (NULL);
|
||||
/*
|
||||
* Skip (span) leading delimiters (s += strspn(s, delim), sort of).
|
||||
*/
|
||||
cont:
|
||||
c = *s++;
|
||||
for(spanp = (char*)delim; (sc = *spanp++) != 0;) {
|
||||
if(c == sc) goto cont;
|
||||
}
|
||||
if(c == 0) { /* no non-delimiter characters */
|
||||
*last = NULL;
|
||||
return (NULL);
|
||||
}
|
||||
tok = s - 1;
|
||||
/*
|
||||
* Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
|
||||
* Note that delim must have one NUL; we stop if we see that, too.
|
||||
*/
|
||||
for(;;) {
|
||||
c = *s++;
|
||||
spanp = (char*)delim;
|
||||
do {
|
||||
if((sc = *spanp++) == c) {
|
||||
if(c == 0)
|
||||
s = NULL;
|
||||
else
|
||||
s[-1] = 0;
|
||||
*last = s;
|
||||
return (tok);
|
||||
}
|
||||
} while(sc != 0);
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
void flipbip_btox(const unsigned char* in, int in_len, char* str) {
|
||||
for(int i = 0; i < in_len; i++) {
|
||||
unsigned char n;
|
||||
unsigned char x = in[i];
|
||||
|
||||
str += 2;
|
||||
*(str + (i * 2)) = '\0';
|
||||
|
||||
for(n = 2; n != 0; --n) {
|
||||
*(--str + (i * 2)) = "0123456789abcdef"[x & 0x0F];
|
||||
x >>= 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
void flipbip_xtob(const char* str, unsigned char* out, int out_len) {
|
||||
int len = strlen(str) / 2;
|
||||
if(len > out_len) len = out_len;
|
||||
for(int i = 0; i < len; i++) {
|
||||
char c = 0;
|
||||
if(str[i * 2] >= '0' && str[i * 2] <= '9') c += (str[i * 2] - '0') << 4;
|
||||
if((str[i * 2] & ~0x20) >= 'A' && (str[i * 2] & ~0x20) <= 'F')
|
||||
c += (10 + (str[i * 2] & ~0x20) - 'A') << 4;
|
||||
if(str[i * 2 + 1] >= '0' && str[i * 2 + 1] <= '9') c += (str[i * 2 + 1] - '0');
|
||||
if((str[i * 2 + 1] & ~0x20) >= 'A' && (str[i * 2 + 1] & ~0x20) <= 'F')
|
||||
c += (10 + (str[i * 2 + 1] & ~0x20) - 'A');
|
||||
out[i] = c;
|
||||
}
|
||||
}
|
||||
|
||||
void flipbip_cipher(
|
||||
const unsigned char* key_in,
|
||||
const unsigned int key_len,
|
||||
const char* in,
|
||||
char* out,
|
||||
const unsigned int io_len) {
|
||||
if(io_len > 512) return;
|
||||
|
||||
RC4_CTX ctx;
|
||||
uint8_t buf[256];
|
||||
memzero(buf, 256);
|
||||
|
||||
flipbip_xtob(in, buf, io_len / 2);
|
||||
|
||||
rc4_init(&ctx, key_in, key_len);
|
||||
rc4_encrypt(&ctx, buf, 256);
|
||||
|
||||
flipbip_btox(buf, io_len / 2, out);
|
||||
|
||||
memzero(buf, 256);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
char* flipbip_strtok(char* s, const char* delim);
|
||||
char* flipbip_strtok_r(char* s, const char* delim, char** last);
|
||||
|
||||
void flipbip_btox(const unsigned char* in, int in_len, char* str);
|
||||
void flipbip_xtob(const char* str, unsigned char* out, int out_len);
|
||||
|
||||
void flipbip_cipher(
|
||||
const unsigned char* key_in,
|
||||
const unsigned int key_len,
|
||||
const char* in,
|
||||
char* out,
|
||||
const unsigned int io_len);
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,2 @@
|
||||
Tomas Dzetkulic <dzetkulic@gmail.com>
|
||||
Pavol Rusnak <stick@gk2.sk>
|
||||
@@ -0,0 +1,17 @@
|
||||
Tomas Dzetkulic <dzetkulic@gmail.com>
|
||||
Pavol Rusnak <stick@gk2.sk>
|
||||
Jochen Hoenicke <hoenicke@gmail.com>
|
||||
Dustin Laurence <dustin@laurences.net>
|
||||
Ondrej Mikle <ondrej.mikle@nic.cz>
|
||||
Roman Zeyde <roman.zeyde@gmail.com>
|
||||
Alex Beregszaszi <alex@rtfs.hu>
|
||||
netanelkl <netanel.keidar@gmail.com>
|
||||
Jan Pochyla <jpochyla@gmail.com>
|
||||
Ondrej Mikle <ondrej.mikle@gmail.com>
|
||||
Josh Billings <jdb6167@rit.edu>
|
||||
Adam Mackler <AdamMackler@gmail.com>
|
||||
Oleg Andreev <oleganza@gmail.com>
|
||||
mog <mog@rush.rldn.net>
|
||||
John Dvorak <johndvorak26@gmail.com>
|
||||
Christian Reitter <invd@inhq.net>
|
||||
Struan Clark <xtruan@users.noreply.github.com>
|
||||
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Tomas Dzetkulic
|
||||
Copyright (c) 2013 Pavol Rusnak
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,187 @@
|
||||
# CLANG_VERSION is empty if the compiler is not clang-based
|
||||
CLANG_VERSION = $(shell $(CC) --version | sed -nr 's/^.*clang version ([0-9.]+).*$$/\1/p')
|
||||
CLANG_VERSION_MAJOR = $(shell echo $(CLANG_VERSION) | cut -f1 -d.)
|
||||
|
||||
# determine specific version ranges
|
||||
ifneq ($(CLANG_VERSION),)
|
||||
$(if $(shell [ $(CLANG_VERSION_MAJOR) -ge 13 ] && echo "OK"), \
|
||||
$(eval CLANG_AT_LEAST_13 := true), \
|
||||
$(eval CLANG_AT_LEAST_13 := false))
|
||||
endif
|
||||
|
||||
ifeq ($(FUZZER),1)
|
||||
CC ?= clang
|
||||
LD ?= $(CC)
|
||||
SANFLAGS += -fsanitize=fuzzer
|
||||
|
||||
# only clang versions >= 13 support this feature
|
||||
ifeq ($(CLANG_AT_LEAST_13),true)
|
||||
$(info "info: using -fsanitize-ignorelist")
|
||||
SANFLAGS += -fsanitize-ignorelist=fuzzer/sanitizer_ignorelist.txt
|
||||
else
|
||||
$(info "info: not using -fsanitize-ignorelist")
|
||||
endif
|
||||
|
||||
# TODO is there a better solution, for example by disabling a specific optimization technique?
|
||||
# there is a clang optimization issue in relation with the blake2 code at -fsanitize=undefined
|
||||
$(warning "warning: disabling optimization on blake2 code as workaround")
|
||||
blake2b.o: OPTFLAGS += -O0
|
||||
blake2s.o: OPTFLAGS += -O0
|
||||
|
||||
else ifeq ($(ADDRESS_SANITIZER),1)
|
||||
SANFLAGS += -fsanitize=address,undefined
|
||||
endif
|
||||
|
||||
CC ?= cc
|
||||
|
||||
OPTFLAGS ?= -O3 -g
|
||||
|
||||
CFLAGS += $(OPTFLAGS) \
|
||||
$(SANFLAGS) \
|
||||
-std=gnu99 \
|
||||
-W \
|
||||
-Wall \
|
||||
-Wextra \
|
||||
-Wimplicit-function-declaration \
|
||||
-Wredundant-decls \
|
||||
-Wstrict-prototypes \
|
||||
-Wundef \
|
||||
-Wshadow \
|
||||
-Wpointer-arith \
|
||||
-Wformat \
|
||||
-Wreturn-type \
|
||||
-Wsign-compare \
|
||||
-Wmultichar \
|
||||
-Wformat-nonliteral \
|
||||
-Winit-self \
|
||||
-Wuninitialized \
|
||||
-Wformat-security \
|
||||
-Wno-missing-braces \
|
||||
-Werror
|
||||
|
||||
ZKP_CFLAGS = \
|
||||
-DECMULT_GEN_PREC_BITS=4 \
|
||||
-DECMULT_WINDOW_SIZE=8 \
|
||||
-DENABLE_MODULE_GENERATOR \
|
||||
-DENABLE_MODULE_RECOVERY \
|
||||
-DENABLE_MODULE_SCHNORRSIG \
|
||||
-DENABLE_MODULE_EXTRAKEYS
|
||||
ZKP_PATH = ../vendor/secp256k1-zkp
|
||||
# this is specific for 64-bit builds
|
||||
CFLAGS += -DSECP256K1_CONTEXT_SIZE=208
|
||||
|
||||
VALGRIND ?= 1
|
||||
ifeq ($(VALGRIND),1)
|
||||
CFLAGS += -DVALGRIND
|
||||
endif
|
||||
|
||||
CFLAGS += -I.
|
||||
CFLAGS += -I..
|
||||
CFLAGS += -DUSE_ETHEREUM=1
|
||||
CFLAGS += -DUSE_KECCAK=1
|
||||
CFLAGS += -DUSE_MONERO=1
|
||||
CFLAGS += -DUSE_NEM=1
|
||||
CFLAGS += -DUSE_CARDANO=1
|
||||
CFLAGS += $(shell pkg-config --cflags openssl)
|
||||
|
||||
# disable certain optimizations and features when small footprint is required
|
||||
ifdef SMALL
|
||||
CFLAGS += -DUSE_PRECOMPUTED_CP=0
|
||||
endif
|
||||
|
||||
SRCS = bignum.c ecdsa.c curves.c secp256k1.c nist256p1.c rand.c hmac.c bip32.c bip39.c bip39_english.c pbkdf2.c base58.c base32.c
|
||||
SRCS += address.c
|
||||
SRCS += script.c
|
||||
SRCS += ripemd160.c
|
||||
SRCS += sha2.c
|
||||
SRCS += sha3.c
|
||||
SRCS += hasher.c
|
||||
SRCS += aes/aescrypt.c aes/aeskey.c aes/aestab.c aes/aes_modes.c
|
||||
SRCS += ed25519_donna/curve25519_donna_32bit.c ed25519_donna/curve25519_donna_helpers.c ed25519_donna/modm_donna_32bit.c
|
||||
SRCS += ed25519_donna/ed25519_donna_basepoint_table.c ed25519_donna/ed25519_donna_32bit_tables.c ed25519_donna/ed25519_donna_impl_base.c
|
||||
SRCS += ed25519_donna/ed25519.c ed25519_donna/curve25519_donna_scalarmult_base.c ed25519_donna/ed25519_sha3.c ed25519_donna/ed25519_keccak.c
|
||||
SRCS += monero/base58.c
|
||||
SRCS += monero/serialize.c
|
||||
SRCS += monero/xmr.c
|
||||
SRCS += blake256.c
|
||||
SRCS += blake2b.c blake2s.c
|
||||
SRCS += chacha_drbg.c
|
||||
SRCS += groestl.c
|
||||
SRCS += chacha20poly1305/chacha20poly1305.c chacha20poly1305/chacha_merged.c chacha20poly1305/poly1305_donna.c chacha20poly1305/rfc7539.c
|
||||
SRCS += rc4.c
|
||||
SRCS += nem.c
|
||||
SRCS += segwit_addr.c cash_addr.c
|
||||
SRCS += memzero.c
|
||||
SRCS += shamir.c
|
||||
SRCS += hmac_drbg.c
|
||||
SRCS += rfc6979.c
|
||||
SRCS += slip39.c
|
||||
SRCS += zkp_context.c
|
||||
SRCS += zkp_ecdsa.c
|
||||
SRCS += zkp_bip340.c
|
||||
SRCS += cardano.c
|
||||
|
||||
OBJS = $(SRCS:.c=.o)
|
||||
OBJS += secp256k1-zkp.o
|
||||
OBJS += precomputed_ecmult.o
|
||||
OBJS += precomputed_ecmult_gen.o
|
||||
|
||||
TESTLIBS = $(shell pkg-config --libs check) -lpthread -lm
|
||||
TESTSSLLIBS = $(shell pkg-config --libs openssl)
|
||||
|
||||
all: tools tests
|
||||
|
||||
%.o: %.c %.h options.h
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
tests: tests/test_check tests/test_openssl tests/test_speed tests/libtrezor-crypto.so tests/aestst
|
||||
|
||||
tests/aestst: aes/aestst.o aes/aescrypt.o aes/aeskey.o aes/aestab.o
|
||||
$(CC) $(CFLAGS) $^ -o $@
|
||||
|
||||
tests/test_check.o: tests/test_check_cardano.h tests/test_check_monero.h tests/test_check_cashaddr.h tests/test_check_segwit.h
|
||||
|
||||
tests/test_check: tests/test_check.o $(OBJS)
|
||||
$(CC) $(CFLAGS) tests/test_check.o $(OBJS) $(TESTLIBS) -o tests/test_check
|
||||
|
||||
tests/test_speed: tests/test_speed.o $(OBJS)
|
||||
$(CC) $(CFLAGS) tests/test_speed.o $(OBJS) -o tests/test_speed
|
||||
|
||||
tests/test_openssl: tests/test_openssl.o $(OBJS)
|
||||
$(CC) $(CFLAGS) tests/test_openssl.o $(OBJS) $(TESTSSLLIBS) -o tests/test_openssl
|
||||
|
||||
tests/libtrezor-crypto.so: $(SRCS) secp256k1-zkp.o precomputed_ecmult.o precomputed_ecmult_gen.o
|
||||
$(CC) $(CFLAGS) -DAES_128 -DAES_192 -fPIC -shared $(SRCS) secp256k1-zkp.o precomputed_ecmult.o precomputed_ecmult_gen.o -o tests/libtrezor-crypto.so
|
||||
|
||||
tools: tools/xpubaddrgen tools/mktable tools/bip39bruteforce
|
||||
|
||||
tools/xpubaddrgen: tools/xpubaddrgen.o $(OBJS)
|
||||
$(CC) $(CFLAGS) tools/xpubaddrgen.o $(OBJS) -o tools/xpubaddrgen
|
||||
|
||||
tools/mktable: tools/mktable.o $(OBJS)
|
||||
$(CC) $(CFLAGS) tools/mktable.o $(OBJS) -o tools/mktable
|
||||
|
||||
tools/bip39bruteforce: tools/bip39bruteforce.o $(OBJS)
|
||||
$(CC) $(CFLAGS) tools/bip39bruteforce.o $(OBJS) -o tools/bip39bruteforce
|
||||
|
||||
fuzzer: fuzzer/fuzzer.o $(OBJS)
|
||||
$(CC) $(CFLAGS) fuzzer/fuzzer.o $(OBJS) -o fuzzer/fuzzer
|
||||
|
||||
precomputed_ecmult.o:
|
||||
$(CC) $(CFLAGS) -Wno-unused-function $(ZKP_CFLAGS) -fPIC -c $(ZKP_PATH)/src/precomputed_ecmult.c -o precomputed_ecmult.o
|
||||
|
||||
precomputed_ecmult_gen.o:
|
||||
$(CC) $(CFLAGS) -Wno-unused-function $(ZKP_CFLAGS) -fPIC -c $(ZKP_PATH)/src/precomputed_ecmult_gen.c -o precomputed_ecmult_gen.o
|
||||
|
||||
secp256k1-zkp.o:
|
||||
$(CC) $(CFLAGS) -Wno-unused-function $(ZKP_CFLAGS) -fPIC -I$(ZKP_PATH) -I$(ZKP_PATH)/src -c $(ZKP_PATH)/src/secp256k1.c -o secp256k1-zkp.o
|
||||
|
||||
clean:
|
||||
rm -f *.o aes/*.o chacha20poly1305/*.o ed25519_donna/*.o monero/*.o
|
||||
rm -f tests/*.o tests/test_check tests/test_speed tests/test_openssl tests/libtrezor-crypto.so tests/aestst
|
||||
rm -f tools/*.o tools/xpubaddrgen tools/mktable tools/bip39bruteforce
|
||||
rm -f fuzzer/*.o fuzzer/fuzzer
|
||||
rm -f secp256k1-zkp.o precomputed_ecmult.o precomputed_ecmult_gen.o
|
||||
|
||||
clean-fuzzer: clean
|
||||
rm -f crash-* fuzz-*.log slow-unit-* timeout-*
|
||||
@@ -0,0 +1,54 @@
|
||||
# trezor-crypto
|
||||
|
||||
Heavily optimized cryptography algorithms for embedded devices.
|
||||
|
||||
These include:
|
||||
- AES/Rijndael encryption/decryption
|
||||
- Big Number (256 bit) Arithmetics
|
||||
- BIP32 Hierarchical Deterministic Wallets
|
||||
- BIP39 Mnemonic code
|
||||
- ECDSA signing/verifying (supports secp256k1 and nist256p1 curves,
|
||||
uses RFC6979 for deterministic signatures)
|
||||
- ECDSA public key derivation
|
||||
- BIP340 Schnorr signature signing/verifying
|
||||
- Base32 (RFC4648 and custom alphabets)
|
||||
- Base58 address representation
|
||||
- Ed25519 signing/verifying (also SHA3 and Keccak variants)
|
||||
- ECDH using secp256k1, nist256p1 and Curve25519
|
||||
- HMAC-SHA256 and HMAC-SHA512
|
||||
- PBKDF2
|
||||
- RIPEMD-160
|
||||
- SHA1
|
||||
- SHA2-256/SHA2-512
|
||||
- SHA3/Keccak
|
||||
- BLAKE2s/BLAKE2b
|
||||
- Chacha20-Poly1305
|
||||
- unit tests (using Check - check.sf.net; in test_check.c)
|
||||
- tests against OpenSSL (in test_openssl.c)
|
||||
- integrated Wycheproof tests
|
||||
|
||||
Distibuted under MIT License.
|
||||
|
||||
## Some parts of the library come from external sources:
|
||||
|
||||
- AES: https://github.com/BrianGladman/aes
|
||||
- Base58: https://github.com/luke-jr/libbase58
|
||||
- BLAKE2s/BLAKE2b: https://github.com/BLAKE2/BLAKE2
|
||||
- RIPEMD-160: https://github.com/ARMmbed/mbedtls
|
||||
- SHA1/SHA2: http://www.aarongifford.com/computers/sha.html
|
||||
- SHA3: https://github.com/rhash/RHash
|
||||
- Curve25519: https://github.com/agl/curve25519-donna
|
||||
- Ed25519: https://github.com/floodyberry/ed25519-donna
|
||||
- Chacha20: https://github.com/wg/c20p1305
|
||||
- Poly1305: https://github.com/floodyberry/poly1305-donna
|
||||
|
||||
## Repo source:
|
||||
|
||||
```
|
||||
remote = git+ssh://git@github.com/trezor/trezor-crypto
|
||||
branch = master
|
||||
commit = 915b3dbbbf58c262865647728a3463b8785fc965
|
||||
parent = 6ad3294f31a1e7484b43c104ff2880b965198cad
|
||||
method = rebase
|
||||
cmdver = 0.4.0
|
||||
```
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Copyright (c) 2016 Daira Hopwood
|
||||
* Copyright (c) 2016 Pavol Rusnak
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "address.h"
|
||||
#include "bignum.h"
|
||||
|
||||
size_t address_prefix_bytes_len(uint32_t address_type) {
|
||||
if(address_type <= 0xFF) return 1;
|
||||
if(address_type <= 0xFFFF) return 2;
|
||||
if(address_type <= 0xFFFFFF) return 3;
|
||||
return 4;
|
||||
}
|
||||
|
||||
void address_write_prefix_bytes(uint32_t address_type, uint8_t* out) {
|
||||
if(address_type > 0xFFFFFF) *(out++) = address_type >> 24;
|
||||
if(address_type > 0xFFFF) *(out++) = (address_type >> 16) & 0xFF;
|
||||
if(address_type > 0xFF) *(out++) = (address_type >> 8) & 0xFF;
|
||||
*(out++) = address_type & 0xFF;
|
||||
}
|
||||
|
||||
bool address_check_prefix(const uint8_t* addr, uint32_t address_type) {
|
||||
if(address_type <= 0xFF) {
|
||||
return address_type == (uint32_t)(addr[0]);
|
||||
}
|
||||
if(address_type <= 0xFFFF) {
|
||||
return address_type == (((uint32_t)addr[0] << 8) | ((uint32_t)addr[1]));
|
||||
}
|
||||
if(address_type <= 0xFFFFFF) {
|
||||
return address_type ==
|
||||
(((uint32_t)addr[0] << 16) | ((uint32_t)addr[1] << 8) | ((uint32_t)addr[2]));
|
||||
}
|
||||
return address_type == (((uint32_t)addr[0] << 24) | ((uint32_t)addr[1] << 16) |
|
||||
((uint32_t)addr[2] << 8) | ((uint32_t)addr[3]));
|
||||
}
|
||||
|
||||
#if USE_ETHEREUM
|
||||
#include "sha3.h"
|
||||
|
||||
void ethereum_address_checksum(const uint8_t* addr, char* address, bool rskip60, uint64_t chain_id) {
|
||||
const char* hex = "0123456789abcdef";
|
||||
address[0] = '0';
|
||||
address[1] = 'x';
|
||||
for(int i = 0; i < 20; i++) {
|
||||
address[2 + i * 2] = hex[(addr[i] >> 4) & 0xF];
|
||||
address[2 + i * 2 + 1] = hex[addr[i] & 0xF];
|
||||
}
|
||||
address[42] = 0;
|
||||
|
||||
SHA3_CTX ctx = {0};
|
||||
uint8_t hash[32] = {0};
|
||||
keccak_256_Init(&ctx);
|
||||
if(rskip60) {
|
||||
char prefix[16] = {0};
|
||||
int prefix_size =
|
||||
bn_format_uint64(chain_id, NULL, "0x", 0, 0, false, 0, prefix, sizeof(prefix));
|
||||
keccak_Update(&ctx, (const uint8_t*)prefix, prefix_size);
|
||||
}
|
||||
keccak_Update(&ctx, (const uint8_t*)(address + 2), 40);
|
||||
keccak_Final(&ctx, hash);
|
||||
|
||||
for(int i = 0; i < 20; i++) {
|
||||
if((hash[i] & 0x80) && address[2 + i * 2] >= 'a' && address[2 + i * 2] <= 'f') {
|
||||
address[2 + i * 2] -= 0x20;
|
||||
}
|
||||
if((hash[i] & 0x08) && address[2 + i * 2 + 1] >= 'a' && address[2 + i * 2 + 1] <= 'f') {
|
||||
address[2 + i * 2 + 1] -= 0x20;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2016 Daira Hopwood
|
||||
* Copyright (c) 2016 Pavol Rusnak
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __ADDRESS_H__
|
||||
#define __ADDRESS_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "options.h"
|
||||
|
||||
size_t address_prefix_bytes_len(uint32_t address_type);
|
||||
void address_write_prefix_bytes(uint32_t address_type, uint8_t* out);
|
||||
bool address_check_prefix(const uint8_t* addr, uint32_t address_type);
|
||||
#if USE_ETHEREUM
|
||||
void ethereum_address_checksum(const uint8_t* addr, char* address, bool rskip60, uint64_t chain_id);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
---------------------------------------------------------------------------
|
||||
Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved.
|
||||
|
||||
The redistribution and use of this software (with or without changes)
|
||||
is allowed without the payment of fees or royalties provided that:
|
||||
|
||||
source code distributions include the above copyright notice, this
|
||||
list of conditions and the following disclaimer;
|
||||
|
||||
binary distributions include the above copyright notice, this list
|
||||
of conditions and the following disclaimer in their documentation.
|
||||
|
||||
This software is provided 'as is' with no explicit or implied warranties
|
||||
in respect of its operation, including, but not limited to, correctness
|
||||
and fitness for purpose.
|
||||
---------------------------------------------------------------------------
|
||||
Issue Date: 02/08/2018
|
||||
|
||||
This file contains the definitions required to use AES in C. See aesopt.h
|
||||
for optimisation details.
|
||||
*/
|
||||
|
||||
#ifndef _AES_H
|
||||
#define _AES_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define VOID_RETURN void
|
||||
#define INT_RETURN int
|
||||
#define ALIGN_OFFSET(x, n) (((intptr_t)(x)) & ((n)-1))
|
||||
#define ALIGN_FLOOR(x, n) ((uint8_t*)(x) - (((intptr_t)(x)) & ((n)-1)))
|
||||
#define ALIGN_CEIL(x, n) ((uint8_t*)(x) + (-((intptr_t)(x)) & ((n)-1)))
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// #define AES_128 /* if a fast 128 bit key scheduler is needed */
|
||||
// #define AES_192 /* if a fast 192 bit key scheduler is needed */
|
||||
#define AES_256 /* if a fast 256 bit key scheduler is needed */
|
||||
// #define AES_VAR /* if variable key size scheduler is needed */
|
||||
#if 1
|
||||
#define AES_MODES /* if support is needed for modes in the C code */
|
||||
#endif /* (these will use AES_NI if it is present) */
|
||||
#if 0 /* add this to make direct calls to the AES_NI */
|
||||
#/* implemented CBC and CTR modes available */
|
||||
#define ADD_AESNI_MODE_CALLS
|
||||
#endif
|
||||
|
||||
/* The following must also be set in assembler files if being used */
|
||||
|
||||
#define AES_ENCRYPT /* if support for encryption is needed */
|
||||
#define AES_DECRYPT /* if support for decryption is needed */
|
||||
|
||||
#define AES_BLOCK_SIZE_P2 4 /* AES block size as a power of 2 */
|
||||
#define AES_BLOCK_SIZE (1 << AES_BLOCK_SIZE_P2) /* AES block size */
|
||||
#define N_COLS 4 /* the number of columns in the state */
|
||||
|
||||
/* The key schedule length is 11, 13 or 15 16-byte blocks for 128, */
|
||||
/* 192 or 256-bit keys respectively. That is 176, 208 or 240 bytes */
|
||||
/* or 44, 52 or 60 32-bit words. */
|
||||
|
||||
#if defined(AES_VAR) || defined(AES_256)
|
||||
#define KS_LENGTH 60
|
||||
#elif defined(AES_192)
|
||||
#define KS_LENGTH 52
|
||||
#else
|
||||
#define KS_LENGTH 44
|
||||
#endif
|
||||
|
||||
#define AES_RETURN INT_RETURN
|
||||
|
||||
/* the character array 'inf' in the following structures is used */
|
||||
/* to hold AES context information. This AES code uses cx->inf.b[0] */
|
||||
/* to hold the number of rounds multiplied by 16. The other three */
|
||||
/* elements can be used by code that implements additional modes */
|
||||
|
||||
typedef union {
|
||||
uint32_t l;
|
||||
uint8_t b[4];
|
||||
} aes_inf;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4324)
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && defined(_WIN64)
|
||||
#define ALIGNED_(x) __declspec(align(x))
|
||||
#elif defined(__GNUC__) && defined(__x86_64__)
|
||||
#define ALIGNED_(x) __attribute__((aligned(x)))
|
||||
#else
|
||||
#define ALIGNED_(x)
|
||||
#endif
|
||||
|
||||
typedef struct ALIGNED_(16) {
|
||||
uint32_t ks[KS_LENGTH];
|
||||
aes_inf inf;
|
||||
} aes_encrypt_ctx;
|
||||
|
||||
typedef struct ALIGNED_(16) {
|
||||
uint32_t ks[KS_LENGTH];
|
||||
aes_inf inf;
|
||||
} aes_decrypt_ctx;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(default : 4324)
|
||||
#endif
|
||||
|
||||
/* This routine must be called before first use if non-static */
|
||||
/* tables are being used */
|
||||
|
||||
AES_RETURN aes_init(void);
|
||||
|
||||
/* Key lengths in the range 16 <= key_len <= 32 are given in bytes, */
|
||||
/* those in the range 128 <= key_len <= 256 are given in bits */
|
||||
|
||||
#if defined(AES_ENCRYPT)
|
||||
|
||||
#if defined(AES_128) || defined(AES_VAR)
|
||||
AES_RETURN aes_encrypt_key128(const unsigned char* key, aes_encrypt_ctx cx[1]);
|
||||
#endif
|
||||
|
||||
#if defined(AES_192) || defined(AES_VAR)
|
||||
AES_RETURN aes_encrypt_key192(const unsigned char* key, aes_encrypt_ctx cx[1]);
|
||||
#endif
|
||||
|
||||
#if defined(AES_256) || defined(AES_VAR)
|
||||
AES_RETURN aes_encrypt_key256(const unsigned char* key, aes_encrypt_ctx cx[1]);
|
||||
#endif
|
||||
|
||||
#if defined(AES_VAR)
|
||||
AES_RETURN aes_encrypt_key(const unsigned char* key, int key_len, aes_encrypt_ctx cx[1]);
|
||||
#endif
|
||||
|
||||
AES_RETURN aes_encrypt(const unsigned char* in, unsigned char* out, const aes_encrypt_ctx cx[1]);
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(AES_DECRYPT)
|
||||
|
||||
#if defined(AES_128) || defined(AES_VAR)
|
||||
AES_RETURN aes_decrypt_key128(const unsigned char* key, aes_decrypt_ctx cx[1]);
|
||||
#endif
|
||||
|
||||
#if defined(AES_192) || defined(AES_VAR)
|
||||
AES_RETURN aes_decrypt_key192(const unsigned char* key, aes_decrypt_ctx cx[1]);
|
||||
#endif
|
||||
|
||||
#if defined(AES_256) || defined(AES_VAR)
|
||||
AES_RETURN aes_decrypt_key256(const unsigned char* key, aes_decrypt_ctx cx[1]);
|
||||
#endif
|
||||
|
||||
#if defined(AES_VAR)
|
||||
AES_RETURN aes_decrypt_key(const unsigned char* key, int key_len, aes_decrypt_ctx cx[1]);
|
||||
#endif
|
||||
|
||||
AES_RETURN aes_decrypt(const unsigned char* in, unsigned char* out, const aes_decrypt_ctx cx[1]);
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(AES_MODES)
|
||||
|
||||
/* Multiple calls to the following subroutines for multiple block */
|
||||
/* ECB, CBC, CFB, OFB and CTR mode encryption can be used to handle */
|
||||
/* long messages incrementally provided that the context AND the iv */
|
||||
/* are preserved between all such calls. For the ECB and CBC modes */
|
||||
/* each individual call within a series of incremental calls must */
|
||||
/* process only full blocks (i.e. len must be a multiple of 16) but */
|
||||
/* the CFB, OFB and CTR mode calls can handle multiple incremental */
|
||||
/* calls of any length. Each mode is reset when a new AES key is */
|
||||
/* set but ECB needs no reset and CBC can be reset without setting */
|
||||
/* a new key by setting a new IV value. To reset CFB, OFB and CTR */
|
||||
/* without setting the key, aes_mode_reset() must be called and the */
|
||||
/* IV must be set. NOTE: All these calls update the IV on exit so */
|
||||
/* this has to be reset if a new operation with the same IV as the */
|
||||
/* previous one is required (or decryption follows encryption with */
|
||||
/* the same IV array). */
|
||||
|
||||
AES_RETURN aes_test_alignment_detection(unsigned int n);
|
||||
|
||||
AES_RETURN aes_ecb_encrypt(
|
||||
const unsigned char* ibuf,
|
||||
unsigned char* obuf,
|
||||
int len,
|
||||
const aes_encrypt_ctx cx[1]);
|
||||
|
||||
AES_RETURN aes_ecb_decrypt(
|
||||
const unsigned char* ibuf,
|
||||
unsigned char* obuf,
|
||||
int len,
|
||||
const aes_decrypt_ctx cx[1]);
|
||||
|
||||
AES_RETURN aes_cbc_encrypt(
|
||||
const unsigned char* ibuf,
|
||||
unsigned char* obuf,
|
||||
int len,
|
||||
unsigned char* iv,
|
||||
const aes_encrypt_ctx cx[1]);
|
||||
|
||||
AES_RETURN aes_cbc_decrypt(
|
||||
const unsigned char* ibuf,
|
||||
unsigned char* obuf,
|
||||
int len,
|
||||
unsigned char* iv,
|
||||
const aes_decrypt_ctx cx[1]);
|
||||
|
||||
AES_RETURN aes_mode_reset(aes_encrypt_ctx cx[1]);
|
||||
|
||||
AES_RETURN aes_cfb_encrypt(
|
||||
const unsigned char* ibuf,
|
||||
unsigned char* obuf,
|
||||
int len,
|
||||
unsigned char* iv,
|
||||
aes_encrypt_ctx cx[1]);
|
||||
|
||||
AES_RETURN aes_cfb_decrypt(
|
||||
const unsigned char* ibuf,
|
||||
unsigned char* obuf,
|
||||
int len,
|
||||
unsigned char* iv,
|
||||
aes_encrypt_ctx cx[1]);
|
||||
|
||||
#define aes_ofb_encrypt aes_ofb_crypt
|
||||
#define aes_ofb_decrypt aes_ofb_crypt
|
||||
|
||||
AES_RETURN aes_ofb_crypt(
|
||||
const unsigned char* ibuf,
|
||||
unsigned char* obuf,
|
||||
int len,
|
||||
unsigned char* iv,
|
||||
aes_encrypt_ctx cx[1]);
|
||||
|
||||
typedef void cbuf_inc(unsigned char* cbuf);
|
||||
|
||||
#define aes_ctr_encrypt aes_ctr_crypt
|
||||
#define aes_ctr_decrypt aes_ctr_crypt
|
||||
|
||||
AES_RETURN aes_ctr_crypt(
|
||||
const unsigned char* ibuf,
|
||||
unsigned char* obuf,
|
||||
int len,
|
||||
unsigned char* cbuf,
|
||||
cbuf_inc ctr_inc,
|
||||
aes_encrypt_ctx cx[1]);
|
||||
|
||||
void aes_ctr_cbuf_inc(unsigned char* cbuf);
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,932 @@
|
||||
/*
|
||||
---------------------------------------------------------------------------
|
||||
Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved.
|
||||
|
||||
The redistribution and use of this software (with or without changes)
|
||||
is allowed without the payment of fees or royalties provided that:
|
||||
|
||||
source code distributions include the above copyright notice, this
|
||||
list of conditions and the following disclaimer;
|
||||
|
||||
binary distributions include the above copyright notice, this list
|
||||
of conditions and the following disclaimer in their documentation.
|
||||
|
||||
This software is provided 'as is' with no explicit or implied warranties
|
||||
in respect of its operation, including, but not limited to, correctness
|
||||
and fitness for purpose.
|
||||
---------------------------------------------------------------------------
|
||||
Issue Date: 20/12/2007
|
||||
|
||||
These subroutines implement multiple block AES modes for ECB, CBC, CFB,
|
||||
OFB and CTR encryption, The code provides support for the VIA Advanced
|
||||
Cryptography Engine (ACE).
|
||||
|
||||
NOTE: In the following subroutines, the AES contexts (ctx) must be
|
||||
16 byte aligned if VIA ACE is being used
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "aesopt.h"
|
||||
|
||||
#if defined(AES_MODES)
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER > 800)
|
||||
#pragma intrinsic(memcpy)
|
||||
#endif
|
||||
|
||||
#define BFR_BLOCKS 8
|
||||
|
||||
/* These values are used to detect long word alignment in order to */
|
||||
/* speed up some buffer operations. This facility may not work on */
|
||||
/* some machines so this define can be commented out if necessary */
|
||||
|
||||
#define FAST_BUFFER_OPERATIONS
|
||||
|
||||
#define lp32(x) ((uint32_t*)(x))
|
||||
|
||||
#if defined(USE_VIA_ACE_IF_PRESENT)
|
||||
|
||||
#include "aes_via_ace.h"
|
||||
|
||||
#pragma pack(16)
|
||||
|
||||
aligned_array(unsigned long, enc_gen_table, 12, 16) = NEH_ENC_GEN_DATA;
|
||||
aligned_array(unsigned long, enc_load_table, 12, 16) = NEH_ENC_LOAD_DATA;
|
||||
aligned_array(unsigned long, enc_hybrid_table, 12, 16) = NEH_ENC_HYBRID_DATA;
|
||||
aligned_array(unsigned long, dec_gen_table, 12, 16) = NEH_DEC_GEN_DATA;
|
||||
aligned_array(unsigned long, dec_load_table, 12, 16) = NEH_DEC_LOAD_DATA;
|
||||
aligned_array(unsigned long, dec_hybrid_table, 12, 16) = NEH_DEC_HYBRID_DATA;
|
||||
|
||||
/* NOTE: These control word macros must only be used after */
|
||||
/* a key has been set up because they depend on key size */
|
||||
/* See the VIA ACE documentation for key type information */
|
||||
/* and aes_via_ace.h for non-default NEH_KEY_TYPE values */
|
||||
|
||||
#ifndef NEH_KEY_TYPE
|
||||
#define NEH_KEY_TYPE NEH_HYBRID
|
||||
#endif
|
||||
|
||||
#if NEH_KEY_TYPE == NEH_LOAD
|
||||
#define kd_adr(c) ((uint8_t*)(c)->ks)
|
||||
#elif NEH_KEY_TYPE == NEH_GENERATE
|
||||
#define kd_adr(c) ((uint8_t*)(c)->ks + (c)->inf.b[0])
|
||||
#elif NEH_KEY_TYPE == NEH_HYBRID
|
||||
#define kd_adr(c) ((uint8_t*)(c)->ks + ((c)->inf.b[0] == 160 ? 160 : 0))
|
||||
#else
|
||||
#error no key type defined for VIA ACE
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#define aligned_array(type, name, no, stride) type name[no]
|
||||
#define aligned_auto(type, name, no, stride) type name[no]
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER > 1200
|
||||
|
||||
#define via_cwd(cwd, ty, dir, len) unsigned long* cwd = (dir##_##ty##_table + ((len - 128) >> 4))
|
||||
|
||||
#else
|
||||
|
||||
#define via_cwd(cwd, ty, dir, len) \
|
||||
aligned_auto(unsigned long, cwd, 4, 16); \
|
||||
cwd[1] = cwd[2] = cwd[3] = 0; \
|
||||
cwd[0] = neh_##dir##_##ty##_key(len)
|
||||
|
||||
#endif
|
||||
|
||||
/* test the code for detecting and setting pointer alignment */
|
||||
|
||||
AES_RETURN aes_test_alignment_detection(unsigned int n) /* 4 <= n <= 16 */
|
||||
{
|
||||
uint8_t p[16];
|
||||
uint32_t i = 0, count_eq = 0, count_neq = 0;
|
||||
|
||||
if(n < 4 || n > 16) return EXIT_FAILURE;
|
||||
|
||||
for(i = 0; i < n; ++i) {
|
||||
uint8_t *qf = ALIGN_FLOOR(p + i, n), *qh = ALIGN_CEIL(p + i, n);
|
||||
|
||||
if(qh == qf)
|
||||
++count_eq;
|
||||
else if(qh == qf + n)
|
||||
++count_neq;
|
||||
else
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return (count_eq != 1 || count_neq != n - 1 ? EXIT_FAILURE : EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
AES_RETURN aes_mode_reset(aes_encrypt_ctx ctx[1]) {
|
||||
ctx->inf.b[2] = 0;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
AES_RETURN aes_ecb_encrypt(
|
||||
const unsigned char* ibuf,
|
||||
unsigned char* obuf,
|
||||
int len,
|
||||
const aes_encrypt_ctx ctx[1]) {
|
||||
int nb = len >> AES_BLOCK_SIZE_P2;
|
||||
|
||||
if(len & (AES_BLOCK_SIZE - 1)) return EXIT_FAILURE;
|
||||
|
||||
#if defined(USE_VIA_ACE_IF_PRESENT)
|
||||
|
||||
if(ctx->inf.b[1] == 0xff) {
|
||||
uint8_t* ksp = (uint8_t*)(ctx->ks);
|
||||
via_cwd(cwd, hybrid, enc, 2 * ctx->inf.b[0] - 192);
|
||||
|
||||
if(ALIGN_OFFSET(ctx, 16)) return EXIT_FAILURE;
|
||||
|
||||
if(!ALIGN_OFFSET(ibuf, 16) && !ALIGN_OFFSET(obuf, 16)) {
|
||||
via_ecb_op5(ksp, cwd, ibuf, obuf, nb);
|
||||
} else {
|
||||
aligned_auto(uint8_t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16);
|
||||
uint8_t *ip = NULL, *op = NULL;
|
||||
|
||||
while(nb) {
|
||||
int m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb);
|
||||
|
||||
ip = (ALIGN_OFFSET(ibuf, 16) ? buf : ibuf);
|
||||
op = (ALIGN_OFFSET(obuf, 16) ? buf : obuf);
|
||||
|
||||
if(ip != ibuf) memcpy(buf, ibuf, m * AES_BLOCK_SIZE);
|
||||
|
||||
via_ecb_op5(ksp, cwd, ip, op, m);
|
||||
|
||||
if(op != obuf) memcpy(obuf, buf, m * AES_BLOCK_SIZE);
|
||||
|
||||
ibuf += m * AES_BLOCK_SIZE;
|
||||
obuf += m * AES_BLOCK_SIZE;
|
||||
nb -= m;
|
||||
}
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if !defined(ASSUME_VIA_ACE_PRESENT)
|
||||
while(nb--) {
|
||||
if(aes_encrypt(ibuf, obuf, ctx) != EXIT_SUCCESS) return EXIT_FAILURE;
|
||||
ibuf += AES_BLOCK_SIZE;
|
||||
obuf += AES_BLOCK_SIZE;
|
||||
}
|
||||
#endif
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
AES_RETURN aes_ecb_decrypt(
|
||||
const unsigned char* ibuf,
|
||||
unsigned char* obuf,
|
||||
int len,
|
||||
const aes_decrypt_ctx ctx[1]) {
|
||||
int nb = len >> AES_BLOCK_SIZE_P2;
|
||||
|
||||
if(len & (AES_BLOCK_SIZE - 1)) return EXIT_FAILURE;
|
||||
|
||||
#if defined(USE_VIA_ACE_IF_PRESENT)
|
||||
|
||||
if(ctx->inf.b[1] == 0xff) {
|
||||
uint8_t* ksp = kd_adr(ctx);
|
||||
via_cwd(cwd, hybrid, dec, 2 * ctx->inf.b[0] - 192);
|
||||
|
||||
if(ALIGN_OFFSET(ctx, 16)) return EXIT_FAILURE;
|
||||
|
||||
if(!ALIGN_OFFSET(ibuf, 16) && !ALIGN_OFFSET(obuf, 16)) {
|
||||
via_ecb_op5(ksp, cwd, ibuf, obuf, nb);
|
||||
} else {
|
||||
aligned_auto(uint8_t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16);
|
||||
uint8_t *ip = NULL, *op = NULL;
|
||||
|
||||
while(nb) {
|
||||
int m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb);
|
||||
|
||||
ip = (ALIGN_OFFSET(ibuf, 16) ? buf : ibuf);
|
||||
op = (ALIGN_OFFSET(obuf, 16) ? buf : obuf);
|
||||
|
||||
if(ip != ibuf) memcpy(buf, ibuf, m * AES_BLOCK_SIZE);
|
||||
|
||||
via_ecb_op5(ksp, cwd, ip, op, m);
|
||||
|
||||
if(op != obuf) memcpy(obuf, buf, m * AES_BLOCK_SIZE);
|
||||
|
||||
ibuf += m * AES_BLOCK_SIZE;
|
||||
obuf += m * AES_BLOCK_SIZE;
|
||||
nb -= m;
|
||||
}
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if !defined(ASSUME_VIA_ACE_PRESENT)
|
||||
while(nb--) {
|
||||
if(aes_decrypt(ibuf, obuf, ctx) != EXIT_SUCCESS) return EXIT_FAILURE;
|
||||
ibuf += AES_BLOCK_SIZE;
|
||||
obuf += AES_BLOCK_SIZE;
|
||||
}
|
||||
#endif
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
AES_RETURN aes_cbc_encrypt(
|
||||
const unsigned char* ibuf,
|
||||
unsigned char* obuf,
|
||||
int len,
|
||||
unsigned char* iv,
|
||||
const aes_encrypt_ctx ctx[1]) {
|
||||
int nb = len >> AES_BLOCK_SIZE_P2;
|
||||
|
||||
if(len & (AES_BLOCK_SIZE - 1)) return EXIT_FAILURE;
|
||||
|
||||
#if defined(USE_VIA_ACE_IF_PRESENT)
|
||||
|
||||
if(ctx->inf.b[1] == 0xff) {
|
||||
uint8_t *ksp = (uint8_t*)(ctx->ks), *ivp = iv;
|
||||
aligned_auto(uint8_t, liv, AES_BLOCK_SIZE, 16);
|
||||
via_cwd(cwd, hybrid, enc, 2 * ctx->inf.b[0] - 192);
|
||||
|
||||
if(ALIGN_OFFSET(ctx, 16)) return EXIT_FAILURE;
|
||||
|
||||
if(ALIGN_OFFSET(iv, 16)) /* ensure an aligned iv */
|
||||
{
|
||||
ivp = liv;
|
||||
memcpy(liv, iv, AES_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
if(!ALIGN_OFFSET(ibuf, 16) && !ALIGN_OFFSET(obuf, 16) && !ALIGN_OFFSET(iv, 16)) {
|
||||
via_cbc_op7(ksp, cwd, ibuf, obuf, nb, ivp, ivp);
|
||||
} else {
|
||||
aligned_auto(uint8_t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16);
|
||||
uint8_t *ip = NULL, *op = NULL;
|
||||
|
||||
while(nb) {
|
||||
int m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb);
|
||||
|
||||
ip = (ALIGN_OFFSET(ibuf, 16) ? buf : ibuf);
|
||||
op = (ALIGN_OFFSET(obuf, 16) ? buf : obuf);
|
||||
|
||||
if(ip != ibuf) memcpy(buf, ibuf, m * AES_BLOCK_SIZE);
|
||||
|
||||
via_cbc_op7(ksp, cwd, ip, op, m, ivp, ivp);
|
||||
|
||||
if(op != obuf) memcpy(obuf, buf, m * AES_BLOCK_SIZE);
|
||||
|
||||
ibuf += m * AES_BLOCK_SIZE;
|
||||
obuf += m * AES_BLOCK_SIZE;
|
||||
nb -= m;
|
||||
}
|
||||
}
|
||||
|
||||
if(iv != ivp) memcpy(iv, ivp, AES_BLOCK_SIZE);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if !defined(ASSUME_VIA_ACE_PRESENT)
|
||||
#ifdef FAST_BUFFER_OPERATIONS
|
||||
if(!ALIGN_OFFSET(ibuf, 4) && !ALIGN_OFFSET(iv, 4))
|
||||
while(nb--) {
|
||||
lp32(iv)[0] ^= lp32(ibuf)[0];
|
||||
lp32(iv)[1] ^= lp32(ibuf)[1];
|
||||
lp32(iv)[2] ^= lp32(ibuf)[2];
|
||||
lp32(iv)[3] ^= lp32(ibuf)[3];
|
||||
if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE;
|
||||
memcpy(obuf, iv, AES_BLOCK_SIZE);
|
||||
ibuf += AES_BLOCK_SIZE;
|
||||
obuf += AES_BLOCK_SIZE;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
while(nb--) {
|
||||
iv[0] ^= ibuf[0];
|
||||
iv[1] ^= ibuf[1];
|
||||
iv[2] ^= ibuf[2];
|
||||
iv[3] ^= ibuf[3];
|
||||
iv[4] ^= ibuf[4];
|
||||
iv[5] ^= ibuf[5];
|
||||
iv[6] ^= ibuf[6];
|
||||
iv[7] ^= ibuf[7];
|
||||
iv[8] ^= ibuf[8];
|
||||
iv[9] ^= ibuf[9];
|
||||
iv[10] ^= ibuf[10];
|
||||
iv[11] ^= ibuf[11];
|
||||
iv[12] ^= ibuf[12];
|
||||
iv[13] ^= ibuf[13];
|
||||
iv[14] ^= ibuf[14];
|
||||
iv[15] ^= ibuf[15];
|
||||
if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE;
|
||||
memcpy(obuf, iv, AES_BLOCK_SIZE);
|
||||
ibuf += AES_BLOCK_SIZE;
|
||||
obuf += AES_BLOCK_SIZE;
|
||||
}
|
||||
#endif
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
AES_RETURN aes_cbc_decrypt(
|
||||
const unsigned char* ibuf,
|
||||
unsigned char* obuf,
|
||||
int len,
|
||||
unsigned char* iv,
|
||||
const aes_decrypt_ctx ctx[1]) {
|
||||
unsigned char tmp[AES_BLOCK_SIZE];
|
||||
int nb = len >> AES_BLOCK_SIZE_P2;
|
||||
|
||||
if(len & (AES_BLOCK_SIZE - 1)) return EXIT_FAILURE;
|
||||
|
||||
#if defined(USE_VIA_ACE_IF_PRESENT)
|
||||
|
||||
if(ctx->inf.b[1] == 0xff) {
|
||||
uint8_t *ksp = kd_adr(ctx), *ivp = iv;
|
||||
aligned_auto(uint8_t, liv, AES_BLOCK_SIZE, 16);
|
||||
via_cwd(cwd, hybrid, dec, 2 * ctx->inf.b[0] - 192);
|
||||
|
||||
if(ALIGN_OFFSET(ctx, 16)) return EXIT_FAILURE;
|
||||
|
||||
if(ALIGN_OFFSET(iv, 16)) /* ensure an aligned iv */
|
||||
{
|
||||
ivp = liv;
|
||||
memcpy(liv, iv, AES_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
if(!ALIGN_OFFSET(ibuf, 16) && !ALIGN_OFFSET(obuf, 16) && !ALIGN_OFFSET(iv, 16)) {
|
||||
via_cbc_op6(ksp, cwd, ibuf, obuf, nb, ivp);
|
||||
} else {
|
||||
aligned_auto(uint8_t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16);
|
||||
uint8_t *ip = NULL, *op = NULL;
|
||||
|
||||
while(nb) {
|
||||
int m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb);
|
||||
|
||||
ip = (ALIGN_OFFSET(ibuf, 16) ? buf : ibuf);
|
||||
op = (ALIGN_OFFSET(obuf, 16) ? buf : obuf);
|
||||
|
||||
if(ip != ibuf) memcpy(buf, ibuf, m * AES_BLOCK_SIZE);
|
||||
|
||||
via_cbc_op6(ksp, cwd, ip, op, m, ivp);
|
||||
|
||||
if(op != obuf) memcpy(obuf, buf, m * AES_BLOCK_SIZE);
|
||||
|
||||
ibuf += m * AES_BLOCK_SIZE;
|
||||
obuf += m * AES_BLOCK_SIZE;
|
||||
nb -= m;
|
||||
}
|
||||
}
|
||||
|
||||
if(iv != ivp) memcpy(iv, ivp, AES_BLOCK_SIZE);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(ASSUME_VIA_ACE_PRESENT)
|
||||
#ifdef FAST_BUFFER_OPERATIONS
|
||||
if(!ALIGN_OFFSET(obuf, 4) && !ALIGN_OFFSET(iv, 4))
|
||||
while(nb--) {
|
||||
memcpy(tmp, ibuf, AES_BLOCK_SIZE);
|
||||
if(aes_decrypt(ibuf, obuf, ctx) != EXIT_SUCCESS) return EXIT_FAILURE;
|
||||
lp32(obuf)[0] ^= lp32(iv)[0];
|
||||
lp32(obuf)[1] ^= lp32(iv)[1];
|
||||
lp32(obuf)[2] ^= lp32(iv)[2];
|
||||
lp32(obuf)[3] ^= lp32(iv)[3];
|
||||
memcpy(iv, tmp, AES_BLOCK_SIZE);
|
||||
ibuf += AES_BLOCK_SIZE;
|
||||
obuf += AES_BLOCK_SIZE;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
while(nb--) {
|
||||
memcpy(tmp, ibuf, AES_BLOCK_SIZE);
|
||||
if(aes_decrypt(ibuf, obuf, ctx) != EXIT_SUCCESS) return EXIT_FAILURE;
|
||||
obuf[0] ^= iv[0];
|
||||
obuf[1] ^= iv[1];
|
||||
obuf[2] ^= iv[2];
|
||||
obuf[3] ^= iv[3];
|
||||
obuf[4] ^= iv[4];
|
||||
obuf[5] ^= iv[5];
|
||||
obuf[6] ^= iv[6];
|
||||
obuf[7] ^= iv[7];
|
||||
obuf[8] ^= iv[8];
|
||||
obuf[9] ^= iv[9];
|
||||
obuf[10] ^= iv[10];
|
||||
obuf[11] ^= iv[11];
|
||||
obuf[12] ^= iv[12];
|
||||
obuf[13] ^= iv[13];
|
||||
obuf[14] ^= iv[14];
|
||||
obuf[15] ^= iv[15];
|
||||
memcpy(iv, tmp, AES_BLOCK_SIZE);
|
||||
ibuf += AES_BLOCK_SIZE;
|
||||
obuf += AES_BLOCK_SIZE;
|
||||
}
|
||||
#endif
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
AES_RETURN aes_cfb_encrypt(
|
||||
const unsigned char* ibuf,
|
||||
unsigned char* obuf,
|
||||
int len,
|
||||
unsigned char* iv,
|
||||
aes_encrypt_ctx ctx[1]) {
|
||||
int cnt = 0, b_pos = (int)ctx->inf.b[2], nb;
|
||||
|
||||
if(b_pos) /* complete any partial block */
|
||||
{
|
||||
while(b_pos < AES_BLOCK_SIZE && cnt < len) {
|
||||
*obuf++ = (iv[b_pos++] ^= *ibuf++);
|
||||
cnt++;
|
||||
}
|
||||
|
||||
b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos);
|
||||
}
|
||||
|
||||
if((nb = (len - cnt) >> AES_BLOCK_SIZE_P2) != 0) /* process whole blocks */
|
||||
{
|
||||
#if defined(USE_VIA_ACE_IF_PRESENT)
|
||||
|
||||
if(ctx->inf.b[1] == 0xff) {
|
||||
int m;
|
||||
uint8_t *ksp = (uint8_t*)(ctx->ks), *ivp = iv;
|
||||
aligned_auto(uint8_t, liv, AES_BLOCK_SIZE, 16);
|
||||
via_cwd(cwd, hybrid, enc, 2 * ctx->inf.b[0] - 192);
|
||||
|
||||
if(ALIGN_OFFSET(ctx, 16)) return EXIT_FAILURE;
|
||||
|
||||
if(ALIGN_OFFSET(iv, 16)) /* ensure an aligned iv */
|
||||
{
|
||||
ivp = liv;
|
||||
memcpy(liv, iv, AES_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
if(!ALIGN_OFFSET(ibuf, 16) && !ALIGN_OFFSET(obuf, 16)) {
|
||||
via_cfb_op7(ksp, cwd, ibuf, obuf, nb, ivp, ivp);
|
||||
ibuf += nb * AES_BLOCK_SIZE;
|
||||
obuf += nb * AES_BLOCK_SIZE;
|
||||
cnt += nb * AES_BLOCK_SIZE;
|
||||
} else /* input, output or both are unaligned */
|
||||
{
|
||||
aligned_auto(uint8_t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16);
|
||||
uint8_t *ip = NULL, *op = NULL;
|
||||
|
||||
while(nb) {
|
||||
m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb), nb -= m;
|
||||
|
||||
ip = (ALIGN_OFFSET(ibuf, 16) ? buf : ibuf);
|
||||
op = (ALIGN_OFFSET(obuf, 16) ? buf : obuf);
|
||||
|
||||
if(ip != ibuf) memcpy(buf, ibuf, m * AES_BLOCK_SIZE);
|
||||
|
||||
via_cfb_op7(ksp, cwd, ip, op, m, ivp, ivp);
|
||||
|
||||
if(op != obuf) memcpy(obuf, buf, m * AES_BLOCK_SIZE);
|
||||
|
||||
ibuf += m * AES_BLOCK_SIZE;
|
||||
obuf += m * AES_BLOCK_SIZE;
|
||||
cnt += m * AES_BLOCK_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
if(ivp != iv) memcpy(iv, ivp, AES_BLOCK_SIZE);
|
||||
}
|
||||
#else
|
||||
#ifdef FAST_BUFFER_OPERATIONS
|
||||
if(!ALIGN_OFFSET(ibuf, 4) && !ALIGN_OFFSET(obuf, 4) && !ALIGN_OFFSET(iv, 4))
|
||||
while(cnt + AES_BLOCK_SIZE <= len) {
|
||||
assert(b_pos == 0);
|
||||
if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE;
|
||||
lp32(obuf)[0] = lp32(iv)[0] ^= lp32(ibuf)[0];
|
||||
lp32(obuf)[1] = lp32(iv)[1] ^= lp32(ibuf)[1];
|
||||
lp32(obuf)[2] = lp32(iv)[2] ^= lp32(ibuf)[2];
|
||||
lp32(obuf)[3] = lp32(iv)[3] ^= lp32(ibuf)[3];
|
||||
ibuf += AES_BLOCK_SIZE;
|
||||
obuf += AES_BLOCK_SIZE;
|
||||
cnt += AES_BLOCK_SIZE;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
while(cnt + AES_BLOCK_SIZE <= len) {
|
||||
assert(b_pos == 0);
|
||||
if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE;
|
||||
obuf[0] = iv[0] ^= ibuf[0];
|
||||
obuf[1] = iv[1] ^= ibuf[1];
|
||||
obuf[2] = iv[2] ^= ibuf[2];
|
||||
obuf[3] = iv[3] ^= ibuf[3];
|
||||
obuf[4] = iv[4] ^= ibuf[4];
|
||||
obuf[5] = iv[5] ^= ibuf[5];
|
||||
obuf[6] = iv[6] ^= ibuf[6];
|
||||
obuf[7] = iv[7] ^= ibuf[7];
|
||||
obuf[8] = iv[8] ^= ibuf[8];
|
||||
obuf[9] = iv[9] ^= ibuf[9];
|
||||
obuf[10] = iv[10] ^= ibuf[10];
|
||||
obuf[11] = iv[11] ^= ibuf[11];
|
||||
obuf[12] = iv[12] ^= ibuf[12];
|
||||
obuf[13] = iv[13] ^= ibuf[13];
|
||||
obuf[14] = iv[14] ^= ibuf[14];
|
||||
obuf[15] = iv[15] ^= ibuf[15];
|
||||
ibuf += AES_BLOCK_SIZE;
|
||||
obuf += AES_BLOCK_SIZE;
|
||||
cnt += AES_BLOCK_SIZE;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
while(cnt < len) {
|
||||
if(!b_pos && aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE;
|
||||
|
||||
while(cnt < len && b_pos < AES_BLOCK_SIZE) {
|
||||
*obuf++ = (iv[b_pos++] ^= *ibuf++);
|
||||
cnt++;
|
||||
}
|
||||
|
||||
b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos);
|
||||
}
|
||||
|
||||
ctx->inf.b[2] = (uint8_t)b_pos;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
AES_RETURN aes_cfb_decrypt(
|
||||
const unsigned char* ibuf,
|
||||
unsigned char* obuf,
|
||||
int len,
|
||||
unsigned char* iv,
|
||||
aes_encrypt_ctx ctx[1]) {
|
||||
int cnt = 0, b_pos = (int)ctx->inf.b[2], nb;
|
||||
|
||||
if(b_pos) /* complete any partial block */
|
||||
{
|
||||
uint8_t t;
|
||||
|
||||
while(b_pos < AES_BLOCK_SIZE && cnt < len) {
|
||||
t = *ibuf++;
|
||||
*obuf++ = t ^ iv[b_pos];
|
||||
iv[b_pos++] = t;
|
||||
cnt++;
|
||||
}
|
||||
|
||||
b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos);
|
||||
}
|
||||
|
||||
if((nb = (len - cnt) >> AES_BLOCK_SIZE_P2) != 0) /* process whole blocks */
|
||||
{
|
||||
#if defined(USE_VIA_ACE_IF_PRESENT)
|
||||
|
||||
if(ctx->inf.b[1] == 0xff) {
|
||||
int m;
|
||||
uint8_t *ksp = (uint8_t*)(ctx->ks), *ivp = iv;
|
||||
aligned_auto(uint8_t, liv, AES_BLOCK_SIZE, 16);
|
||||
via_cwd(cwd, hybrid, dec, 2 * ctx->inf.b[0] - 192);
|
||||
|
||||
if(ALIGN_OFFSET(ctx, 16)) return EXIT_FAILURE;
|
||||
|
||||
if(ALIGN_OFFSET(iv, 16)) /* ensure an aligned iv */
|
||||
{
|
||||
ivp = liv;
|
||||
memcpy(liv, iv, AES_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
if(!ALIGN_OFFSET(ibuf, 16) && !ALIGN_OFFSET(obuf, 16)) {
|
||||
via_cfb_op6(ksp, cwd, ibuf, obuf, nb, ivp);
|
||||
ibuf += nb * AES_BLOCK_SIZE;
|
||||
obuf += nb * AES_BLOCK_SIZE;
|
||||
cnt += nb * AES_BLOCK_SIZE;
|
||||
} else /* input, output or both are unaligned */
|
||||
{
|
||||
aligned_auto(uint8_t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16);
|
||||
uint8_t *ip = NULL, *op = NULL;
|
||||
|
||||
while(nb) {
|
||||
m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb), nb -= m;
|
||||
|
||||
ip = (ALIGN_OFFSET(ibuf, 16) ? buf : ibuf);
|
||||
op = (ALIGN_OFFSET(obuf, 16) ? buf : obuf);
|
||||
|
||||
if(ip != ibuf) /* input buffer is not aligned */
|
||||
memcpy(buf, ibuf, m * AES_BLOCK_SIZE);
|
||||
|
||||
via_cfb_op6(ksp, cwd, ip, op, m, ivp);
|
||||
|
||||
if(op != obuf) /* output buffer is not aligned */
|
||||
memcpy(obuf, buf, m * AES_BLOCK_SIZE);
|
||||
|
||||
ibuf += m * AES_BLOCK_SIZE;
|
||||
obuf += m * AES_BLOCK_SIZE;
|
||||
cnt += m * AES_BLOCK_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
if(ivp != iv) memcpy(iv, ivp, AES_BLOCK_SIZE);
|
||||
}
|
||||
#else
|
||||
#ifdef FAST_BUFFER_OPERATIONS
|
||||
if(!ALIGN_OFFSET(ibuf, 4) && !ALIGN_OFFSET(obuf, 4) && !ALIGN_OFFSET(iv, 4))
|
||||
while(cnt + AES_BLOCK_SIZE <= len) {
|
||||
uint32_t t;
|
||||
|
||||
assert(b_pos == 0);
|
||||
if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE;
|
||||
t = lp32(ibuf)[0], lp32(obuf)[0] = t ^ lp32(iv)[0], lp32(iv)[0] = t;
|
||||
t = lp32(ibuf)[1], lp32(obuf)[1] = t ^ lp32(iv)[1], lp32(iv)[1] = t;
|
||||
t = lp32(ibuf)[2], lp32(obuf)[2] = t ^ lp32(iv)[2], lp32(iv)[2] = t;
|
||||
t = lp32(ibuf)[3], lp32(obuf)[3] = t ^ lp32(iv)[3], lp32(iv)[3] = t;
|
||||
ibuf += AES_BLOCK_SIZE;
|
||||
obuf += AES_BLOCK_SIZE;
|
||||
cnt += AES_BLOCK_SIZE;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
while(cnt + AES_BLOCK_SIZE <= len) {
|
||||
uint8_t t;
|
||||
|
||||
assert(b_pos == 0);
|
||||
if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE;
|
||||
t = ibuf[0], obuf[0] = t ^ iv[0], iv[0] = t;
|
||||
t = ibuf[1], obuf[1] = t ^ iv[1], iv[1] = t;
|
||||
t = ibuf[2], obuf[2] = t ^ iv[2], iv[2] = t;
|
||||
t = ibuf[3], obuf[3] = t ^ iv[3], iv[3] = t;
|
||||
t = ibuf[4], obuf[4] = t ^ iv[4], iv[4] = t;
|
||||
t = ibuf[5], obuf[5] = t ^ iv[5], iv[5] = t;
|
||||
t = ibuf[6], obuf[6] = t ^ iv[6], iv[6] = t;
|
||||
t = ibuf[7], obuf[7] = t ^ iv[7], iv[7] = t;
|
||||
t = ibuf[8], obuf[8] = t ^ iv[8], iv[8] = t;
|
||||
t = ibuf[9], obuf[9] = t ^ iv[9], iv[9] = t;
|
||||
t = ibuf[10], obuf[10] = t ^ iv[10], iv[10] = t;
|
||||
t = ibuf[11], obuf[11] = t ^ iv[11], iv[11] = t;
|
||||
t = ibuf[12], obuf[12] = t ^ iv[12], iv[12] = t;
|
||||
t = ibuf[13], obuf[13] = t ^ iv[13], iv[13] = t;
|
||||
t = ibuf[14], obuf[14] = t ^ iv[14], iv[14] = t;
|
||||
t = ibuf[15], obuf[15] = t ^ iv[15], iv[15] = t;
|
||||
ibuf += AES_BLOCK_SIZE;
|
||||
obuf += AES_BLOCK_SIZE;
|
||||
cnt += AES_BLOCK_SIZE;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
while(cnt < len) {
|
||||
uint8_t t;
|
||||
|
||||
if(!b_pos && aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE;
|
||||
|
||||
while(cnt < len && b_pos < AES_BLOCK_SIZE) {
|
||||
t = *ibuf++;
|
||||
*obuf++ = t ^ iv[b_pos];
|
||||
iv[b_pos++] = t;
|
||||
cnt++;
|
||||
}
|
||||
|
||||
b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos);
|
||||
}
|
||||
|
||||
ctx->inf.b[2] = (uint8_t)b_pos;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
AES_RETURN aes_ofb_crypt(
|
||||
const unsigned char* ibuf,
|
||||
unsigned char* obuf,
|
||||
int len,
|
||||
unsigned char* iv,
|
||||
aes_encrypt_ctx ctx[1]) {
|
||||
int cnt = 0, b_pos = (int)ctx->inf.b[2], nb;
|
||||
|
||||
if(b_pos) /* complete any partial block */
|
||||
{
|
||||
while(b_pos < AES_BLOCK_SIZE && cnt < len) {
|
||||
*obuf++ = iv[b_pos++] ^ *ibuf++;
|
||||
cnt++;
|
||||
}
|
||||
|
||||
b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos);
|
||||
}
|
||||
|
||||
if((nb = (len - cnt) >> AES_BLOCK_SIZE_P2) != 0) /* process whole blocks */
|
||||
{
|
||||
#if defined(USE_VIA_ACE_IF_PRESENT)
|
||||
|
||||
if(ctx->inf.b[1] == 0xff) {
|
||||
int m;
|
||||
uint8_t *ksp = (uint8_t*)(ctx->ks), *ivp = iv;
|
||||
aligned_auto(uint8_t, liv, AES_BLOCK_SIZE, 16);
|
||||
via_cwd(cwd, hybrid, enc, 2 * ctx->inf.b[0] - 192);
|
||||
|
||||
if(ALIGN_OFFSET(ctx, 16)) return EXIT_FAILURE;
|
||||
|
||||
if(ALIGN_OFFSET(iv, 16)) /* ensure an aligned iv */
|
||||
{
|
||||
ivp = liv;
|
||||
memcpy(liv, iv, AES_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
if(!ALIGN_OFFSET(ibuf, 16) && !ALIGN_OFFSET(obuf, 16)) {
|
||||
via_ofb_op6(ksp, cwd, ibuf, obuf, nb, ivp);
|
||||
ibuf += nb * AES_BLOCK_SIZE;
|
||||
obuf += nb * AES_BLOCK_SIZE;
|
||||
cnt += nb * AES_BLOCK_SIZE;
|
||||
} else /* input, output or both are unaligned */
|
||||
{
|
||||
aligned_auto(uint8_t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16);
|
||||
uint8_t *ip = NULL, *op = NULL;
|
||||
|
||||
while(nb) {
|
||||
m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb), nb -= m;
|
||||
|
||||
ip = (ALIGN_OFFSET(ibuf, 16) ? buf : ibuf);
|
||||
op = (ALIGN_OFFSET(obuf, 16) ? buf : obuf);
|
||||
|
||||
if(ip != ibuf) memcpy(buf, ibuf, m * AES_BLOCK_SIZE);
|
||||
|
||||
via_ofb_op6(ksp, cwd, ip, op, m, ivp);
|
||||
|
||||
if(op != obuf) memcpy(obuf, buf, m * AES_BLOCK_SIZE);
|
||||
|
||||
ibuf += m * AES_BLOCK_SIZE;
|
||||
obuf += m * AES_BLOCK_SIZE;
|
||||
cnt += m * AES_BLOCK_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
if(ivp != iv) memcpy(iv, ivp, AES_BLOCK_SIZE);
|
||||
}
|
||||
#else
|
||||
#ifdef FAST_BUFFER_OPERATIONS
|
||||
if(!ALIGN_OFFSET(ibuf, 4) && !ALIGN_OFFSET(obuf, 4) && !ALIGN_OFFSET(iv, 4))
|
||||
while(cnt + AES_BLOCK_SIZE <= len) {
|
||||
assert(b_pos == 0);
|
||||
if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE;
|
||||
lp32(obuf)[0] = lp32(iv)[0] ^ lp32(ibuf)[0];
|
||||
lp32(obuf)[1] = lp32(iv)[1] ^ lp32(ibuf)[1];
|
||||
lp32(obuf)[2] = lp32(iv)[2] ^ lp32(ibuf)[2];
|
||||
lp32(obuf)[3] = lp32(iv)[3] ^ lp32(ibuf)[3];
|
||||
ibuf += AES_BLOCK_SIZE;
|
||||
obuf += AES_BLOCK_SIZE;
|
||||
cnt += AES_BLOCK_SIZE;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
while(cnt + AES_BLOCK_SIZE <= len) {
|
||||
assert(b_pos == 0);
|
||||
if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE;
|
||||
obuf[0] = iv[0] ^ ibuf[0];
|
||||
obuf[1] = iv[1] ^ ibuf[1];
|
||||
obuf[2] = iv[2] ^ ibuf[2];
|
||||
obuf[3] = iv[3] ^ ibuf[3];
|
||||
obuf[4] = iv[4] ^ ibuf[4];
|
||||
obuf[5] = iv[5] ^ ibuf[5];
|
||||
obuf[6] = iv[6] ^ ibuf[6];
|
||||
obuf[7] = iv[7] ^ ibuf[7];
|
||||
obuf[8] = iv[8] ^ ibuf[8];
|
||||
obuf[9] = iv[9] ^ ibuf[9];
|
||||
obuf[10] = iv[10] ^ ibuf[10];
|
||||
obuf[11] = iv[11] ^ ibuf[11];
|
||||
obuf[12] = iv[12] ^ ibuf[12];
|
||||
obuf[13] = iv[13] ^ ibuf[13];
|
||||
obuf[14] = iv[14] ^ ibuf[14];
|
||||
obuf[15] = iv[15] ^ ibuf[15];
|
||||
ibuf += AES_BLOCK_SIZE;
|
||||
obuf += AES_BLOCK_SIZE;
|
||||
cnt += AES_BLOCK_SIZE;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
while(cnt < len) {
|
||||
if(!b_pos && aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE;
|
||||
|
||||
while(cnt < len && b_pos < AES_BLOCK_SIZE) {
|
||||
*obuf++ = iv[b_pos++] ^ *ibuf++;
|
||||
cnt++;
|
||||
}
|
||||
|
||||
b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos);
|
||||
}
|
||||
|
||||
ctx->inf.b[2] = (uint8_t)b_pos;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#define BFR_LENGTH (BFR_BLOCKS * AES_BLOCK_SIZE)
|
||||
|
||||
AES_RETURN aes_ctr_crypt(
|
||||
const unsigned char* ibuf,
|
||||
unsigned char* obuf,
|
||||
int len,
|
||||
unsigned char* cbuf,
|
||||
cbuf_inc ctr_inc,
|
||||
aes_encrypt_ctx ctx[1]) {
|
||||
unsigned char* ip;
|
||||
int i = 0, blen = 0, b_pos = (int)(ctx->inf.b[2]);
|
||||
|
||||
#if defined(USE_VIA_ACE_IF_PRESENT)
|
||||
aligned_auto(uint8_t, buf, BFR_LENGTH, 16);
|
||||
if(ctx->inf.b[1] == 0xff && ALIGN_OFFSET(ctx, 16)) return EXIT_FAILURE;
|
||||
#else
|
||||
uint8_t buf[BFR_LENGTH] = {0};
|
||||
#endif
|
||||
|
||||
if(b_pos) {
|
||||
memcpy(buf, cbuf, AES_BLOCK_SIZE);
|
||||
if(aes_ecb_encrypt(buf, buf, AES_BLOCK_SIZE, ctx) != EXIT_SUCCESS) return EXIT_FAILURE;
|
||||
|
||||
while(b_pos < AES_BLOCK_SIZE && len) {
|
||||
*obuf++ = *ibuf++ ^ buf[b_pos++];
|
||||
--len;
|
||||
}
|
||||
|
||||
if(len) ctr_inc(cbuf), b_pos = 0;
|
||||
}
|
||||
|
||||
while(len) {
|
||||
blen = (len > BFR_LENGTH ? BFR_LENGTH : len), len -= blen;
|
||||
|
||||
for(i = 0, ip = buf; i < (blen >> AES_BLOCK_SIZE_P2); ++i) {
|
||||
memcpy(ip, cbuf, AES_BLOCK_SIZE);
|
||||
ctr_inc(cbuf);
|
||||
ip += AES_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
if(blen & (AES_BLOCK_SIZE - 1)) memcpy(ip, cbuf, AES_BLOCK_SIZE), i++;
|
||||
|
||||
#if defined(USE_VIA_ACE_IF_PRESENT)
|
||||
if(ctx->inf.b[1] == 0xff) {
|
||||
via_cwd(cwd, hybrid, enc, 2 * ctx->inf.b[0] - 192);
|
||||
via_ecb_op5((ctx->ks), cwd, buf, buf, i);
|
||||
} else
|
||||
#endif
|
||||
if(aes_ecb_encrypt(buf, buf, i * AES_BLOCK_SIZE, ctx) != EXIT_SUCCESS)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
i = 0;
|
||||
ip = buf;
|
||||
#ifdef FAST_BUFFER_OPERATIONS
|
||||
if(!ALIGN_OFFSET(ibuf, 4) && !ALIGN_OFFSET(obuf, 4) && !ALIGN_OFFSET(ip, 4))
|
||||
while(i + AES_BLOCK_SIZE <= blen) {
|
||||
lp32(obuf)[0] = lp32(ibuf)[0] ^ lp32(ip)[0];
|
||||
lp32(obuf)[1] = lp32(ibuf)[1] ^ lp32(ip)[1];
|
||||
lp32(obuf)[2] = lp32(ibuf)[2] ^ lp32(ip)[2];
|
||||
lp32(obuf)[3] = lp32(ibuf)[3] ^ lp32(ip)[3];
|
||||
i += AES_BLOCK_SIZE;
|
||||
ip += AES_BLOCK_SIZE;
|
||||
ibuf += AES_BLOCK_SIZE;
|
||||
obuf += AES_BLOCK_SIZE;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
while(i + AES_BLOCK_SIZE <= blen) {
|
||||
obuf[0] = ibuf[0] ^ ip[0];
|
||||
obuf[1] = ibuf[1] ^ ip[1];
|
||||
obuf[2] = ibuf[2] ^ ip[2];
|
||||
obuf[3] = ibuf[3] ^ ip[3];
|
||||
obuf[4] = ibuf[4] ^ ip[4];
|
||||
obuf[5] = ibuf[5] ^ ip[5];
|
||||
obuf[6] = ibuf[6] ^ ip[6];
|
||||
obuf[7] = ibuf[7] ^ ip[7];
|
||||
obuf[8] = ibuf[8] ^ ip[8];
|
||||
obuf[9] = ibuf[9] ^ ip[9];
|
||||
obuf[10] = ibuf[10] ^ ip[10];
|
||||
obuf[11] = ibuf[11] ^ ip[11];
|
||||
obuf[12] = ibuf[12] ^ ip[12];
|
||||
obuf[13] = ibuf[13] ^ ip[13];
|
||||
obuf[14] = ibuf[14] ^ ip[14];
|
||||
obuf[15] = ibuf[15] ^ ip[15];
|
||||
i += AES_BLOCK_SIZE;
|
||||
ip += AES_BLOCK_SIZE;
|
||||
ibuf += AES_BLOCK_SIZE;
|
||||
obuf += AES_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
while(i++ < blen) *obuf++ = *ibuf++ ^ ip[b_pos++];
|
||||
}
|
||||
|
||||
ctx->inf.b[2] = (uint8_t)b_pos;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
void aes_ctr_cbuf_inc(unsigned char* cbuf) {
|
||||
int i = AES_BLOCK_SIZE - 1;
|
||||
while(i >= 0) {
|
||||
cbuf[i]++;
|
||||
if(cbuf[i]) return; // if there was no overflow
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
---------------------------------------------------------------------------
|
||||
Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved.
|
||||
|
||||
The redistribution and use of this software (with or without changes)
|
||||
is allowed without the payment of fees or royalties provided that:
|
||||
|
||||
source code distributions include the above copyright notice, this
|
||||
list of conditions and the following disclaimer;
|
||||
|
||||
binary distributions include the above copyright notice, this list
|
||||
of conditions and the following disclaimer in their documentation.
|
||||
|
||||
This software is provided 'as is' with no explicit or implied warranties
|
||||
in respect of its operation, including, but not limited to, correctness
|
||||
and fitness for purpose.
|
||||
---------------------------------------------------------------------------
|
||||
Issue Date: 20/12/2007
|
||||
*/
|
||||
|
||||
#include "aesopt.h"
|
||||
#include "aestab.h"
|
||||
|
||||
#if defined(USE_INTEL_AES_IF_PRESENT)
|
||||
#include "aes_ni.h"
|
||||
#else
|
||||
/* map names here to provide the external API ('name' -> 'aes_name') */
|
||||
#define aes_xi(x) aes_##x
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define si(y, x, k, c) (s(y, c) = word_in(x, c) ^ (k)[c])
|
||||
#define so(y, x, c) word_out(y, c, s(x, c))
|
||||
|
||||
#if defined(ARRAYS)
|
||||
#define locals(y, x) x[4], y[4]
|
||||
#else
|
||||
#define locals(y, x) x##0, x##1, x##2, x##3, y##0, y##1, y##2, y##3
|
||||
#endif
|
||||
|
||||
#define l_copy(y, x) \
|
||||
s(y, 0) = s(x, 0); \
|
||||
s(y, 1) = s(x, 1); \
|
||||
s(y, 2) = s(x, 2); \
|
||||
s(y, 3) = s(x, 3);
|
||||
#define state_in(y, x, k) \
|
||||
si(y, x, k, 0); \
|
||||
si(y, x, k, 1); \
|
||||
si(y, x, k, 2); \
|
||||
si(y, x, k, 3)
|
||||
#define state_out(y, x) \
|
||||
so(y, x, 0); \
|
||||
so(y, x, 1); \
|
||||
so(y, x, 2); \
|
||||
so(y, x, 3)
|
||||
#define round(rm, y, x, k) \
|
||||
rm(y, x, k, 0); \
|
||||
rm(y, x, k, 1); \
|
||||
rm(y, x, k, 2); \
|
||||
rm(y, x, k, 3)
|
||||
|
||||
#if(FUNCS_IN_C & ENCRYPTION_IN_C)
|
||||
|
||||
/* Visual C++ .Net v7.1 provides the fastest encryption code when using
|
||||
Pentium optimiation with small code but this is poor for decryption
|
||||
so we need to control this with the following VC++ pragmas
|
||||
*/
|
||||
|
||||
#if defined(_MSC_VER) && !defined(_WIN64) && !defined(__clang__)
|
||||
#pragma optimize("s", on)
|
||||
#endif
|
||||
|
||||
/* Given the column (c) of the output state variable, the following
|
||||
macros give the input state variables which are needed in its
|
||||
computation for each row (r) of the state. All the alternative
|
||||
macros give the same end values but expand into different ways
|
||||
of calculating these values. In particular the complex macro
|
||||
used for dynamically variable block sizes is designed to expand
|
||||
to a compile time constant whenever possible but will expand to
|
||||
conditional clauses on some branches (I am grateful to Frank
|
||||
Yellin for this construction)
|
||||
*/
|
||||
|
||||
#define fwd_var(x, r, c) \
|
||||
(r == 0 ? (c == 0 ? s(x, 0) : \
|
||||
c == 1 ? s(x, 1) : \
|
||||
c == 2 ? s(x, 2) : \
|
||||
s(x, 3)) : \
|
||||
r == 1 ? (c == 0 ? s(x, 1) : \
|
||||
c == 1 ? s(x, 2) : \
|
||||
c == 2 ? s(x, 3) : \
|
||||
s(x, 0)) : \
|
||||
r == 2 ? (c == 0 ? s(x, 2) : \
|
||||
c == 1 ? s(x, 3) : \
|
||||
c == 2 ? s(x, 0) : \
|
||||
s(x, 1)) : \
|
||||
(c == 0 ? s(x, 3) : \
|
||||
c == 1 ? s(x, 0) : \
|
||||
c == 2 ? s(x, 1) : \
|
||||
s(x, 2)))
|
||||
|
||||
#if defined(FT4_SET)
|
||||
#undef dec_fmvars
|
||||
#define fwd_rnd(y, x, k, c) (s(y, c) = (k)[c] ^ four_tables(x, t_use(f, n), fwd_var, rf1, c))
|
||||
#elif defined(FT1_SET)
|
||||
#undef dec_fmvars
|
||||
#define fwd_rnd(y, x, k, c) (s(y, c) = (k)[c] ^ one_table(x, upr, t_use(f, n), fwd_var, rf1, c))
|
||||
#else
|
||||
#define fwd_rnd(y, x, k, c) \
|
||||
(s(y, c) = (k)[c] ^ fwd_mcol(no_table(x, t_use(s, box), fwd_var, rf1, c)))
|
||||
#endif
|
||||
|
||||
#if defined(FL4_SET)
|
||||
#define fwd_lrnd(y, x, k, c) (s(y, c) = (k)[c] ^ four_tables(x, t_use(f, l), fwd_var, rf1, c))
|
||||
#elif defined(FL1_SET)
|
||||
#define fwd_lrnd(y, x, k, c) (s(y, c) = (k)[c] ^ one_table(x, ups, t_use(f, l), fwd_var, rf1, c))
|
||||
#else
|
||||
#define fwd_lrnd(y, x, k, c) (s(y, c) = (k)[c] ^ no_table(x, t_use(s, box), fwd_var, rf1, c))
|
||||
#endif
|
||||
|
||||
AES_RETURN
|
||||
aes_xi(encrypt)(const unsigned char* in, unsigned char* out, const aes_encrypt_ctx cx[1]) {
|
||||
uint32_t locals(b0, b1);
|
||||
const uint32_t* kp = NULL;
|
||||
#if defined(dec_fmvars)
|
||||
dec_fmvars; /* declare variables for fwd_mcol() if needed */
|
||||
#endif
|
||||
|
||||
if(cx->inf.b[0] != 10 * AES_BLOCK_SIZE && cx->inf.b[0] != 12 * AES_BLOCK_SIZE &&
|
||||
cx->inf.b[0] != 14 * AES_BLOCK_SIZE)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
kp = cx->ks;
|
||||
state_in(b0, in, kp);
|
||||
|
||||
#if(ENC_UNROLL == FULL)
|
||||
|
||||
switch(cx->inf.b[0]) {
|
||||
case 14 * AES_BLOCK_SIZE:
|
||||
round(fwd_rnd, b1, b0, kp + 1 * N_COLS);
|
||||
round(fwd_rnd, b0, b1, kp + 2 * N_COLS);
|
||||
kp += 2 * N_COLS;
|
||||
//-fallthrough
|
||||
case 12 * AES_BLOCK_SIZE:
|
||||
round(fwd_rnd, b1, b0, kp + 1 * N_COLS);
|
||||
round(fwd_rnd, b0, b1, kp + 2 * N_COLS);
|
||||
kp += 2 * N_COLS;
|
||||
//-fallthrough
|
||||
case 10 * AES_BLOCK_SIZE:
|
||||
round(fwd_rnd, b1, b0, kp + 1 * N_COLS);
|
||||
round(fwd_rnd, b0, b1, kp + 2 * N_COLS);
|
||||
round(fwd_rnd, b1, b0, kp + 3 * N_COLS);
|
||||
round(fwd_rnd, b0, b1, kp + 4 * N_COLS);
|
||||
round(fwd_rnd, b1, b0, kp + 5 * N_COLS);
|
||||
round(fwd_rnd, b0, b1, kp + 6 * N_COLS);
|
||||
round(fwd_rnd, b1, b0, kp + 7 * N_COLS);
|
||||
round(fwd_rnd, b0, b1, kp + 8 * N_COLS);
|
||||
round(fwd_rnd, b1, b0, kp + 9 * N_COLS);
|
||||
round(fwd_lrnd, b0, b1, kp + 10 * N_COLS);
|
||||
//-fallthrough
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#if(ENC_UNROLL == PARTIAL)
|
||||
{
|
||||
uint32_t rnd;
|
||||
for(rnd = 0; rnd < (cx->inf.b[0] >> 5) - 1; ++rnd) {
|
||||
kp += N_COLS;
|
||||
round(fwd_rnd, b1, b0, kp);
|
||||
kp += N_COLS;
|
||||
round(fwd_rnd, b0, b1, kp);
|
||||
}
|
||||
kp += N_COLS;
|
||||
round(fwd_rnd, b1, b0, kp);
|
||||
#else
|
||||
{
|
||||
uint32_t rnd;
|
||||
for(rnd = 0; rnd < (cx->inf.b[0] >> 4) - 1; ++rnd) {
|
||||
kp += N_COLS;
|
||||
round(fwd_rnd, b1, b0, kp);
|
||||
l_copy(b0, b1);
|
||||
}
|
||||
#endif
|
||||
kp += N_COLS;
|
||||
round(fwd_lrnd, b0, b1, kp);
|
||||
}
|
||||
#endif
|
||||
|
||||
state_out(out, b0);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if(FUNCS_IN_C & DECRYPTION_IN_C)
|
||||
|
||||
/* Visual C++ .Net v7.1 provides the fastest encryption code when using
|
||||
Pentium optimiation with small code but this is poor for decryption
|
||||
so we need to control this with the following VC++ pragmas
|
||||
*/
|
||||
|
||||
#if defined(_MSC_VER) && !defined(_WIN64) && !defined(__clang__)
|
||||
#pragma optimize("t", on)
|
||||
#endif
|
||||
|
||||
/* Given the column (c) of the output state variable, the following
|
||||
macros give the input state variables which are needed in its
|
||||
computation for each row (r) of the state. All the alternative
|
||||
macros give the same end values but expand into different ways
|
||||
of calculating these values. In particular the complex macro
|
||||
used for dynamically variable block sizes is designed to expand
|
||||
to a compile time constant whenever possible but will expand to
|
||||
conditional clauses on some branches (I am grateful to Frank
|
||||
Yellin for this construction)
|
||||
*/
|
||||
|
||||
#define inv_var(x, r, c) \
|
||||
(r == 0 ? (c == 0 ? s(x, 0) : \
|
||||
c == 1 ? s(x, 1) : \
|
||||
c == 2 ? s(x, 2) : \
|
||||
s(x, 3)) : \
|
||||
r == 1 ? (c == 0 ? s(x, 3) : \
|
||||
c == 1 ? s(x, 0) : \
|
||||
c == 2 ? s(x, 1) : \
|
||||
s(x, 2)) : \
|
||||
r == 2 ? (c == 0 ? s(x, 2) : \
|
||||
c == 1 ? s(x, 3) : \
|
||||
c == 2 ? s(x, 0) : \
|
||||
s(x, 1)) : \
|
||||
(c == 0 ? s(x, 1) : \
|
||||
c == 1 ? s(x, 2) : \
|
||||
c == 2 ? s(x, 3) : \
|
||||
s(x, 0)))
|
||||
|
||||
#if defined(IT4_SET)
|
||||
#undef dec_imvars
|
||||
#define inv_rnd(y, x, k, c) (s(y, c) = (k)[c] ^ four_tables(x, t_use(i, n), inv_var, rf1, c))
|
||||
#elif defined(IT1_SET)
|
||||
#undef dec_imvars
|
||||
#define inv_rnd(y, x, k, c) (s(y, c) = (k)[c] ^ one_table(x, upr, t_use(i, n), inv_var, rf1, c))
|
||||
#else
|
||||
#define inv_rnd(y, x, k, c) \
|
||||
(s(y, c) = inv_mcol((k)[c] ^ no_table(x, t_use(i, box), inv_var, rf1, c)))
|
||||
#endif
|
||||
|
||||
#if defined(IL4_SET)
|
||||
#define inv_lrnd(y, x, k, c) (s(y, c) = (k)[c] ^ four_tables(x, t_use(i, l), inv_var, rf1, c))
|
||||
#elif defined(IL1_SET)
|
||||
#define inv_lrnd(y, x, k, c) (s(y, c) = (k)[c] ^ one_table(x, ups, t_use(i, l), inv_var, rf1, c))
|
||||
#else
|
||||
#define inv_lrnd(y, x, k, c) (s(y, c) = (k)[c] ^ no_table(x, t_use(i, box), inv_var, rf1, c))
|
||||
#endif
|
||||
|
||||
/* This code can work with the decryption key schedule in the */
|
||||
/* order that is used for encrytpion (where the 1st decryption */
|
||||
/* round key is at the high end ot the schedule) or with a key */
|
||||
/* schedule that has been reversed to put the 1st decryption */
|
||||
/* round key at the low end of the schedule in memory (when */
|
||||
/* AES_REV_DKS is defined) */
|
||||
|
||||
#ifdef AES_REV_DKS
|
||||
#define key_ofs 0
|
||||
#define rnd_key(n) (kp + n * N_COLS)
|
||||
#else
|
||||
#define key_ofs 1
|
||||
#define rnd_key(n) (kp - n * N_COLS)
|
||||
#endif
|
||||
|
||||
AES_RETURN
|
||||
aes_xi(decrypt)(const unsigned char* in, unsigned char* out, const aes_decrypt_ctx cx[1]) {
|
||||
uint32_t locals(b0, b1);
|
||||
#if defined(dec_imvars)
|
||||
dec_imvars; /* declare variables for inv_mcol() if needed */
|
||||
#endif
|
||||
const uint32_t* kp = NULL;
|
||||
|
||||
if(cx->inf.b[0] != 10 * AES_BLOCK_SIZE && cx->inf.b[0] != 12 * AES_BLOCK_SIZE &&
|
||||
cx->inf.b[0] != 14 * AES_BLOCK_SIZE)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
kp = cx->ks + (key_ofs ? (cx->inf.b[0] >> 2) : 0);
|
||||
state_in(b0, in, kp);
|
||||
|
||||
#if(DEC_UNROLL == FULL)
|
||||
|
||||
kp = cx->ks + (key_ofs ? 0 : (cx->inf.b[0] >> 2));
|
||||
switch(cx->inf.b[0]) {
|
||||
case 14 * AES_BLOCK_SIZE:
|
||||
round(inv_rnd, b1, b0, rnd_key(-13));
|
||||
round(inv_rnd, b0, b1, rnd_key(-12));
|
||||
//-fallthrough
|
||||
case 12 * AES_BLOCK_SIZE:
|
||||
round(inv_rnd, b1, b0, rnd_key(-11));
|
||||
round(inv_rnd, b0, b1, rnd_key(-10));
|
||||
//-fallthrough
|
||||
case 10 * AES_BLOCK_SIZE:
|
||||
round(inv_rnd, b1, b0, rnd_key(-9));
|
||||
round(inv_rnd, b0, b1, rnd_key(-8));
|
||||
round(inv_rnd, b1, b0, rnd_key(-7));
|
||||
round(inv_rnd, b0, b1, rnd_key(-6));
|
||||
round(inv_rnd, b1, b0, rnd_key(-5));
|
||||
round(inv_rnd, b0, b1, rnd_key(-4));
|
||||
round(inv_rnd, b1, b0, rnd_key(-3));
|
||||
round(inv_rnd, b0, b1, rnd_key(-2));
|
||||
round(inv_rnd, b1, b0, rnd_key(-1));
|
||||
round(inv_lrnd, b0, b1, rnd_key(0));
|
||||
//-fallthrough
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#if(DEC_UNROLL == PARTIAL)
|
||||
{
|
||||
uint32_t rnd;
|
||||
for(rnd = 0; rnd < (cx->inf.b[0] >> 5) - 1; ++rnd) {
|
||||
kp = rnd_key(1);
|
||||
round(inv_rnd, b1, b0, kp);
|
||||
kp = rnd_key(1);
|
||||
round(inv_rnd, b0, b1, kp);
|
||||
}
|
||||
kp = rnd_key(1);
|
||||
round(inv_rnd, b1, b0, kp);
|
||||
#else
|
||||
{
|
||||
uint32_t rnd;
|
||||
for(rnd = 0; rnd < (cx->inf.b[0] >> 4) - 1; ++rnd) {
|
||||
kp = rnd_key(1);
|
||||
round(inv_rnd, b1, b0, kp);
|
||||
l_copy(b0, b1);
|
||||
}
|
||||
#endif
|
||||
kp = rnd_key(1);
|
||||
round(inv_lrnd, b0, b1, kp);
|
||||
}
|
||||
#endif
|
||||
|
||||
state_out(out, b0);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,662 @@
|
||||
/*
|
||||
---------------------------------------------------------------------------
|
||||
Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved.
|
||||
|
||||
The redistribution and use of this software (with or without changes)
|
||||
is allowed without the payment of fees or royalties provided that:
|
||||
|
||||
source code distributions include the above copyright notice, this
|
||||
list of conditions and the following disclaimer;
|
||||
|
||||
binary distributions include the above copyright notice, this list
|
||||
of conditions and the following disclaimer in their documentation.
|
||||
|
||||
This software is provided 'as is' with no explicit or implied warranties
|
||||
in respect of its operation, including, but not limited to, correctness
|
||||
and fitness for purpose.
|
||||
---------------------------------------------------------------------------
|
||||
Issue Date: 20/12/2007
|
||||
*/
|
||||
|
||||
#include "aesopt.h"
|
||||
#include "aestab.h"
|
||||
|
||||
#if defined(USE_INTEL_AES_IF_PRESENT)
|
||||
#include "aes_ni.h"
|
||||
#else
|
||||
/* map names here to provide the external API ('name' -> 'aes_name') */
|
||||
#define aes_xi(x) aes_##x
|
||||
#endif
|
||||
|
||||
#ifdef USE_VIA_ACE_IF_PRESENT
|
||||
#include "aes_via_ace.h"
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Initialise the key schedule from the user supplied key. The key
|
||||
length can be specified in bytes, with legal values of 16, 24
|
||||
and 32, or in bits, with legal values of 128, 192 and 256. These
|
||||
values correspond with Nk values of 4, 6 and 8 respectively.
|
||||
|
||||
The following macros implement a single cycle in the key
|
||||
schedule generation process. The number of cycles needed
|
||||
for each cx->n_col and nk value is:
|
||||
|
||||
nk = 4 5 6 7 8
|
||||
------------------------------
|
||||
cx->n_col = 4 10 9 8 7 7
|
||||
cx->n_col = 5 14 11 10 9 9
|
||||
cx->n_col = 6 19 15 12 11 11
|
||||
cx->n_col = 7 21 19 16 13 14
|
||||
cx->n_col = 8 29 23 19 17 14
|
||||
*/
|
||||
|
||||
#if defined(REDUCE_CODE_SIZE)
|
||||
#define ls_box ls_sub
|
||||
uint32_t ls_sub(const uint32_t t, const uint32_t n);
|
||||
#define inv_mcol im_sub
|
||||
uint32_t im_sub(const uint32_t x);
|
||||
#ifdef ENC_KS_UNROLL
|
||||
#undef ENC_KS_UNROLL
|
||||
#endif
|
||||
#ifdef DEC_KS_UNROLL
|
||||
#undef DEC_KS_UNROLL
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if(FUNCS_IN_C & ENC_KEYING_IN_C)
|
||||
|
||||
#if defined(AES_128) || defined(AES_VAR)
|
||||
|
||||
#define ke4(k, i) \
|
||||
{ \
|
||||
k[4 * (i) + 4] = ss[0] ^= ls_box(ss[3], 3) ^ t_use(r, c)[i]; \
|
||||
k[4 * (i) + 5] = ss[1] ^= ss[0]; \
|
||||
k[4 * (i) + 6] = ss[2] ^= ss[1]; \
|
||||
k[4 * (i) + 7] = ss[3] ^= ss[2]; \
|
||||
}
|
||||
|
||||
AES_RETURN aes_xi(encrypt_key128)(const unsigned char* key, aes_encrypt_ctx cx[1]) {
|
||||
uint32_t ss[4];
|
||||
|
||||
cx->ks[0] = ss[0] = word_in(key, 0);
|
||||
cx->ks[1] = ss[1] = word_in(key, 1);
|
||||
cx->ks[2] = ss[2] = word_in(key, 2);
|
||||
cx->ks[3] = ss[3] = word_in(key, 3);
|
||||
|
||||
#ifdef ENC_KS_UNROLL
|
||||
ke4(cx->ks, 0);
|
||||
ke4(cx->ks, 1);
|
||||
ke4(cx->ks, 2);
|
||||
ke4(cx->ks, 3);
|
||||
ke4(cx->ks, 4);
|
||||
ke4(cx->ks, 5);
|
||||
ke4(cx->ks, 6);
|
||||
ke4(cx->ks, 7);
|
||||
ke4(cx->ks, 8);
|
||||
#else
|
||||
{
|
||||
uint32_t i;
|
||||
for(i = 0; i < 9; ++i) ke4(cx->ks, i);
|
||||
}
|
||||
#endif
|
||||
ke4(cx->ks, 9);
|
||||
cx->inf.l = 0;
|
||||
cx->inf.b[0] = 10 * AES_BLOCK_SIZE;
|
||||
|
||||
#ifdef USE_VIA_ACE_IF_PRESENT
|
||||
if(VIA_ACE_AVAILABLE) cx->inf.b[1] = 0xff;
|
||||
#endif
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(AES_192) || defined(AES_VAR)
|
||||
|
||||
#define kef6(k, i) \
|
||||
{ \
|
||||
k[6 * (i) + 6] = ss[0] ^= ls_box(ss[5], 3) ^ t_use(r, c)[i]; \
|
||||
k[6 * (i) + 7] = ss[1] ^= ss[0]; \
|
||||
k[6 * (i) + 8] = ss[2] ^= ss[1]; \
|
||||
k[6 * (i) + 9] = ss[3] ^= ss[2]; \
|
||||
}
|
||||
|
||||
#define ke6(k, i) \
|
||||
{ \
|
||||
kef6(k, i); \
|
||||
k[6 * (i) + 10] = ss[4] ^= ss[3]; \
|
||||
k[6 * (i) + 11] = ss[5] ^= ss[4]; \
|
||||
}
|
||||
|
||||
AES_RETURN aes_xi(encrypt_key192)(const unsigned char* key, aes_encrypt_ctx cx[1]) {
|
||||
uint32_t ss[6];
|
||||
|
||||
cx->ks[0] = ss[0] = word_in(key, 0);
|
||||
cx->ks[1] = ss[1] = word_in(key, 1);
|
||||
cx->ks[2] = ss[2] = word_in(key, 2);
|
||||
cx->ks[3] = ss[3] = word_in(key, 3);
|
||||
cx->ks[4] = ss[4] = word_in(key, 4);
|
||||
cx->ks[5] = ss[5] = word_in(key, 5);
|
||||
|
||||
#ifdef ENC_KS_UNROLL
|
||||
ke6(cx->ks, 0);
|
||||
ke6(cx->ks, 1);
|
||||
ke6(cx->ks, 2);
|
||||
ke6(cx->ks, 3);
|
||||
ke6(cx->ks, 4);
|
||||
ke6(cx->ks, 5);
|
||||
ke6(cx->ks, 6);
|
||||
#else
|
||||
{
|
||||
uint32_t i;
|
||||
for(i = 0; i < 7; ++i) ke6(cx->ks, i);
|
||||
}
|
||||
#endif
|
||||
kef6(cx->ks, 7);
|
||||
cx->inf.l = 0;
|
||||
cx->inf.b[0] = 12 * AES_BLOCK_SIZE;
|
||||
|
||||
#ifdef USE_VIA_ACE_IF_PRESENT
|
||||
if(VIA_ACE_AVAILABLE) cx->inf.b[1] = 0xff;
|
||||
#endif
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(AES_256) || defined(AES_VAR)
|
||||
|
||||
#define kef8(k, i) \
|
||||
{ \
|
||||
k[8 * (i) + 8] = ss[0] ^= ls_box(ss[7], 3) ^ t_use(r, c)[i]; \
|
||||
k[8 * (i) + 9] = ss[1] ^= ss[0]; \
|
||||
k[8 * (i) + 10] = ss[2] ^= ss[1]; \
|
||||
k[8 * (i) + 11] = ss[3] ^= ss[2]; \
|
||||
}
|
||||
|
||||
#define ke8(k, i) \
|
||||
{ \
|
||||
kef8(k, i); \
|
||||
k[8 * (i) + 12] = ss[4] ^= ls_box(ss[3], 0); \
|
||||
k[8 * (i) + 13] = ss[5] ^= ss[4]; \
|
||||
k[8 * (i) + 14] = ss[6] ^= ss[5]; \
|
||||
k[8 * (i) + 15] = ss[7] ^= ss[6]; \
|
||||
}
|
||||
|
||||
AES_RETURN aes_xi(encrypt_key256)(const unsigned char* key, aes_encrypt_ctx cx[1]) {
|
||||
uint32_t ss[8];
|
||||
|
||||
cx->ks[0] = ss[0] = word_in(key, 0);
|
||||
cx->ks[1] = ss[1] = word_in(key, 1);
|
||||
cx->ks[2] = ss[2] = word_in(key, 2);
|
||||
cx->ks[3] = ss[3] = word_in(key, 3);
|
||||
cx->ks[4] = ss[4] = word_in(key, 4);
|
||||
cx->ks[5] = ss[5] = word_in(key, 5);
|
||||
cx->ks[6] = ss[6] = word_in(key, 6);
|
||||
cx->ks[7] = ss[7] = word_in(key, 7);
|
||||
|
||||
#ifdef ENC_KS_UNROLL
|
||||
ke8(cx->ks, 0);
|
||||
ke8(cx->ks, 1);
|
||||
ke8(cx->ks, 2);
|
||||
ke8(cx->ks, 3);
|
||||
ke8(cx->ks, 4);
|
||||
ke8(cx->ks, 5);
|
||||
#else
|
||||
{
|
||||
uint32_t i;
|
||||
for(i = 0; i < 6; ++i) ke8(cx->ks, i);
|
||||
}
|
||||
#endif
|
||||
kef8(cx->ks, 6);
|
||||
cx->inf.l = 0;
|
||||
cx->inf.b[0] = 14 * AES_BLOCK_SIZE;
|
||||
|
||||
#ifdef USE_VIA_ACE_IF_PRESENT
|
||||
if(VIA_ACE_AVAILABLE) cx->inf.b[1] = 0xff;
|
||||
#endif
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if(FUNCS_IN_C & DEC_KEYING_IN_C)
|
||||
|
||||
/* this is used to store the decryption round keys */
|
||||
/* in forward or reverse order */
|
||||
|
||||
#ifdef AES_REV_DKS
|
||||
#define v(n, i) ((n) - (i) + 2 * ((i)&3))
|
||||
#else
|
||||
#define v(n, i) (i)
|
||||
#endif
|
||||
|
||||
#if DEC_ROUND == NO_TABLES
|
||||
#define ff(x) (x)
|
||||
#else
|
||||
#define ff(x) inv_mcol(x)
|
||||
#if defined(dec_imvars)
|
||||
#define d_vars dec_imvars
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(AES_128) || defined(AES_VAR)
|
||||
|
||||
#define k4e(k, i) \
|
||||
{ \
|
||||
k[v(40, (4 * (i)) + 4)] = ss[0] ^= ls_box(ss[3], 3) ^ t_use(r, c)[i]; \
|
||||
k[v(40, (4 * (i)) + 5)] = ss[1] ^= ss[0]; \
|
||||
k[v(40, (4 * (i)) + 6)] = ss[2] ^= ss[1]; \
|
||||
k[v(40, (4 * (i)) + 7)] = ss[3] ^= ss[2]; \
|
||||
}
|
||||
|
||||
#if 1
|
||||
|
||||
#define kdf4(k, i) \
|
||||
{ \
|
||||
ss[0] = ss[0] ^ ss[2] ^ ss[1] ^ ss[3]; \
|
||||
ss[1] = ss[1] ^ ss[3]; \
|
||||
ss[2] = ss[2] ^ ss[3]; \
|
||||
ss[4] = ls_box(ss[(i + 3) % 4], 3) ^ t_use(r, c)[i]; \
|
||||
ss[i % 4] ^= ss[4]; \
|
||||
ss[4] ^= k[v(40, (4 * (i)))]; \
|
||||
k[v(40, (4 * (i)) + 4)] = ff(ss[4]); \
|
||||
ss[4] ^= k[v(40, (4 * (i)) + 1)]; \
|
||||
k[v(40, (4 * (i)) + 5)] = ff(ss[4]); \
|
||||
ss[4] ^= k[v(40, (4 * (i)) + 2)]; \
|
||||
k[v(40, (4 * (i)) + 6)] = ff(ss[4]); \
|
||||
ss[4] ^= k[v(40, (4 * (i)) + 3)]; \
|
||||
k[v(40, (4 * (i)) + 7)] = ff(ss[4]); \
|
||||
}
|
||||
|
||||
#define kd4(k, i) \
|
||||
{ \
|
||||
ss[4] = ls_box(ss[(i + 3) % 4], 3) ^ t_use(r, c)[i]; \
|
||||
ss[i % 4] ^= ss[4]; \
|
||||
ss[4] = ff(ss[4]); \
|
||||
k[v(40, (4 * (i)) + 4)] = ss[4] ^= k[v(40, (4 * (i)))]; \
|
||||
k[v(40, (4 * (i)) + 5)] = ss[4] ^= k[v(40, (4 * (i)) + 1)]; \
|
||||
k[v(40, (4 * (i)) + 6)] = ss[4] ^= k[v(40, (4 * (i)) + 2)]; \
|
||||
k[v(40, (4 * (i)) + 7)] = ss[4] ^= k[v(40, (4 * (i)) + 3)]; \
|
||||
}
|
||||
|
||||
#define kdl4(k, i) \
|
||||
{ \
|
||||
ss[4] = ls_box(ss[(i + 3) % 4], 3) ^ t_use(r, c)[i]; \
|
||||
ss[i % 4] ^= ss[4]; \
|
||||
k[v(40, (4 * (i)) + 4)] = (ss[0] ^= ss[1]) ^ ss[2] ^ ss[3]; \
|
||||
k[v(40, (4 * (i)) + 5)] = ss[1] ^ ss[3]; \
|
||||
k[v(40, (4 * (i)) + 6)] = ss[0]; \
|
||||
k[v(40, (4 * (i)) + 7)] = ss[1]; \
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define kdf4(k, i) \
|
||||
{ \
|
||||
ss[0] ^= ls_box(ss[3], 3) ^ t_use(r, c)[i]; \
|
||||
k[v(40, (4 * (i)) + 4)] = ff(ss[0]); \
|
||||
ss[1] ^= ss[0]; \
|
||||
k[v(40, (4 * (i)) + 5)] = ff(ss[1]); \
|
||||
ss[2] ^= ss[1]; \
|
||||
k[v(40, (4 * (i)) + 6)] = ff(ss[2]); \
|
||||
ss[3] ^= ss[2]; \
|
||||
k[v(40, (4 * (i)) + 7)] = ff(ss[3]); \
|
||||
}
|
||||
|
||||
#define kd4(k, i) \
|
||||
{ \
|
||||
ss[4] = ls_box(ss[3], 3) ^ t_use(r, c)[i]; \
|
||||
ss[0] ^= ss[4]; \
|
||||
ss[4] = ff(ss[4]); \
|
||||
k[v(40, (4 * (i)) + 4)] = ss[4] ^= k[v(40, (4 * (i)))]; \
|
||||
ss[1] ^= ss[0]; \
|
||||
k[v(40, (4 * (i)) + 5)] = ss[4] ^= k[v(40, (4 * (i)) + 1)]; \
|
||||
ss[2] ^= ss[1]; \
|
||||
k[v(40, (4 * (i)) + 6)] = ss[4] ^= k[v(40, (4 * (i)) + 2)]; \
|
||||
ss[3] ^= ss[2]; \
|
||||
k[v(40, (4 * (i)) + 7)] = ss[4] ^= k[v(40, (4 * (i)) + 3)]; \
|
||||
}
|
||||
|
||||
#define kdl4(k, i) \
|
||||
{ \
|
||||
ss[0] ^= ls_box(ss[3], 3) ^ t_use(r, c)[i]; \
|
||||
k[v(40, (4 * (i)) + 4)] = ss[0]; \
|
||||
ss[1] ^= ss[0]; \
|
||||
k[v(40, (4 * (i)) + 5)] = ss[1]; \
|
||||
ss[2] ^= ss[1]; \
|
||||
k[v(40, (4 * (i)) + 6)] = ss[2]; \
|
||||
ss[3] ^= ss[2]; \
|
||||
k[v(40, (4 * (i)) + 7)] = ss[3]; \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
AES_RETURN aes_xi(decrypt_key128)(const unsigned char* key, aes_decrypt_ctx cx[1]) {
|
||||
uint32_t ss[5];
|
||||
#if defined(d_vars)
|
||||
d_vars;
|
||||
#endif
|
||||
|
||||
cx->ks[v(40, (0))] = ss[0] = word_in(key, 0);
|
||||
cx->ks[v(40, (1))] = ss[1] = word_in(key, 1);
|
||||
cx->ks[v(40, (2))] = ss[2] = word_in(key, 2);
|
||||
cx->ks[v(40, (3))] = ss[3] = word_in(key, 3);
|
||||
|
||||
#ifdef DEC_KS_UNROLL
|
||||
kdf4(cx->ks, 0);
|
||||
kd4(cx->ks, 1);
|
||||
kd4(cx->ks, 2);
|
||||
kd4(cx->ks, 3);
|
||||
kd4(cx->ks, 4);
|
||||
kd4(cx->ks, 5);
|
||||
kd4(cx->ks, 6);
|
||||
kd4(cx->ks, 7);
|
||||
kd4(cx->ks, 8);
|
||||
kdl4(cx->ks, 9);
|
||||
#else
|
||||
{
|
||||
uint32_t i;
|
||||
for(i = 0; i < 10; ++i) k4e(cx->ks, i);
|
||||
#if !(DEC_ROUND == NO_TABLES)
|
||||
for(i = N_COLS; i < 10 * N_COLS; ++i) cx->ks[i] = inv_mcol(cx->ks[i]);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
cx->inf.l = 0;
|
||||
cx->inf.b[0] = 10 * AES_BLOCK_SIZE;
|
||||
|
||||
#ifdef USE_VIA_ACE_IF_PRESENT
|
||||
if(VIA_ACE_AVAILABLE) cx->inf.b[1] = 0xff;
|
||||
#endif
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(AES_192) || defined(AES_VAR)
|
||||
|
||||
#define k6ef(k, i) \
|
||||
{ \
|
||||
k[v(48, (6 * (i)) + 6)] = ss[0] ^= ls_box(ss[5], 3) ^ t_use(r, c)[i]; \
|
||||
k[v(48, (6 * (i)) + 7)] = ss[1] ^= ss[0]; \
|
||||
k[v(48, (6 * (i)) + 8)] = ss[2] ^= ss[1]; \
|
||||
k[v(48, (6 * (i)) + 9)] = ss[3] ^= ss[2]; \
|
||||
}
|
||||
|
||||
#define k6e(k, i) \
|
||||
{ \
|
||||
k6ef(k, i); \
|
||||
k[v(48, (6 * (i)) + 10)] = ss[4] ^= ss[3]; \
|
||||
k[v(48, (6 * (i)) + 11)] = ss[5] ^= ss[4]; \
|
||||
}
|
||||
|
||||
#define kdf6(k, i) \
|
||||
{ \
|
||||
ss[0] ^= ls_box(ss[5], 3) ^ t_use(r, c)[i]; \
|
||||
k[v(48, (6 * (i)) + 6)] = ff(ss[0]); \
|
||||
ss[1] ^= ss[0]; \
|
||||
k[v(48, (6 * (i)) + 7)] = ff(ss[1]); \
|
||||
ss[2] ^= ss[1]; \
|
||||
k[v(48, (6 * (i)) + 8)] = ff(ss[2]); \
|
||||
ss[3] ^= ss[2]; \
|
||||
k[v(48, (6 * (i)) + 9)] = ff(ss[3]); \
|
||||
ss[4] ^= ss[3]; \
|
||||
k[v(48, (6 * (i)) + 10)] = ff(ss[4]); \
|
||||
ss[5] ^= ss[4]; \
|
||||
k[v(48, (6 * (i)) + 11)] = ff(ss[5]); \
|
||||
}
|
||||
|
||||
#define kd6(k, i) \
|
||||
{ \
|
||||
ss[6] = ls_box(ss[5], 3) ^ t_use(r, c)[i]; \
|
||||
ss[0] ^= ss[6]; \
|
||||
ss[6] = ff(ss[6]); \
|
||||
k[v(48, (6 * (i)) + 6)] = ss[6] ^= k[v(48, (6 * (i)))]; \
|
||||
ss[1] ^= ss[0]; \
|
||||
k[v(48, (6 * (i)) + 7)] = ss[6] ^= k[v(48, (6 * (i)) + 1)]; \
|
||||
ss[2] ^= ss[1]; \
|
||||
k[v(48, (6 * (i)) + 8)] = ss[6] ^= k[v(48, (6 * (i)) + 2)]; \
|
||||
ss[3] ^= ss[2]; \
|
||||
k[v(48, (6 * (i)) + 9)] = ss[6] ^= k[v(48, (6 * (i)) + 3)]; \
|
||||
ss[4] ^= ss[3]; \
|
||||
k[v(48, (6 * (i)) + 10)] = ss[6] ^= k[v(48, (6 * (i)) + 4)]; \
|
||||
ss[5] ^= ss[4]; \
|
||||
k[v(48, (6 * (i)) + 11)] = ss[6] ^= k[v(48, (6 * (i)) + 5)]; \
|
||||
}
|
||||
|
||||
#define kdl6(k, i) \
|
||||
{ \
|
||||
ss[0] ^= ls_box(ss[5], 3) ^ t_use(r, c)[i]; \
|
||||
k[v(48, (6 * (i)) + 6)] = ss[0]; \
|
||||
ss[1] ^= ss[0]; \
|
||||
k[v(48, (6 * (i)) + 7)] = ss[1]; \
|
||||
ss[2] ^= ss[1]; \
|
||||
k[v(48, (6 * (i)) + 8)] = ss[2]; \
|
||||
ss[3] ^= ss[2]; \
|
||||
k[v(48, (6 * (i)) + 9)] = ss[3]; \
|
||||
}
|
||||
|
||||
AES_RETURN aes_xi(decrypt_key192)(const unsigned char* key, aes_decrypt_ctx cx[1]) {
|
||||
uint32_t ss[7];
|
||||
#if defined(d_vars)
|
||||
d_vars;
|
||||
#endif
|
||||
|
||||
cx->ks[v(48, (0))] = ss[0] = word_in(key, 0);
|
||||
cx->ks[v(48, (1))] = ss[1] = word_in(key, 1);
|
||||
cx->ks[v(48, (2))] = ss[2] = word_in(key, 2);
|
||||
cx->ks[v(48, (3))] = ss[3] = word_in(key, 3);
|
||||
|
||||
#ifdef DEC_KS_UNROLL
|
||||
ss[4] = word_in(key, 4);
|
||||
ss[5] = word_in(key, 5);
|
||||
cx->ks[v(48, (4))] = ff(ss[4]);
|
||||
cx->ks[v(48, (5))] = ff(ss[5]);
|
||||
kdf6(cx->ks, 0);
|
||||
kd6(cx->ks, 1);
|
||||
kd6(cx->ks, 2);
|
||||
kd6(cx->ks, 3);
|
||||
kd6(cx->ks, 4);
|
||||
kd6(cx->ks, 5);
|
||||
kd6(cx->ks, 6);
|
||||
kdl6(cx->ks, 7);
|
||||
#else
|
||||
cx->ks[v(48, (4))] = ss[4] = word_in(key, 4);
|
||||
cx->ks[v(48, (5))] = ss[5] = word_in(key, 5);
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for(i = 0; i < 7; ++i) k6e(cx->ks, i);
|
||||
k6ef(cx->ks, 7);
|
||||
#if !(DEC_ROUND == NO_TABLES)
|
||||
for(i = N_COLS; i < 12 * N_COLS; ++i) cx->ks[i] = inv_mcol(cx->ks[i]);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
cx->inf.l = 0;
|
||||
cx->inf.b[0] = 12 * AES_BLOCK_SIZE;
|
||||
|
||||
#ifdef USE_VIA_ACE_IF_PRESENT
|
||||
if(VIA_ACE_AVAILABLE) cx->inf.b[1] = 0xff;
|
||||
#endif
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(AES_256) || defined(AES_VAR)
|
||||
|
||||
#define k8ef(k, i) \
|
||||
{ \
|
||||
k[v(56, (8 * (i)) + 8)] = ss[0] ^= ls_box(ss[7], 3) ^ t_use(r, c)[i]; \
|
||||
k[v(56, (8 * (i)) + 9)] = ss[1] ^= ss[0]; \
|
||||
k[v(56, (8 * (i)) + 10)] = ss[2] ^= ss[1]; \
|
||||
k[v(56, (8 * (i)) + 11)] = ss[3] ^= ss[2]; \
|
||||
}
|
||||
|
||||
#define k8e(k, i) \
|
||||
{ \
|
||||
k8ef(k, i); \
|
||||
k[v(56, (8 * (i)) + 12)] = ss[4] ^= ls_box(ss[3], 0); \
|
||||
k[v(56, (8 * (i)) + 13)] = ss[5] ^= ss[4]; \
|
||||
k[v(56, (8 * (i)) + 14)] = ss[6] ^= ss[5]; \
|
||||
k[v(56, (8 * (i)) + 15)] = ss[7] ^= ss[6]; \
|
||||
}
|
||||
|
||||
#define kdf8(k, i) \
|
||||
{ \
|
||||
ss[0] ^= ls_box(ss[7], 3) ^ t_use(r, c)[i]; \
|
||||
k[v(56, (8 * (i)) + 8)] = ff(ss[0]); \
|
||||
ss[1] ^= ss[0]; \
|
||||
k[v(56, (8 * (i)) + 9)] = ff(ss[1]); \
|
||||
ss[2] ^= ss[1]; \
|
||||
k[v(56, (8 * (i)) + 10)] = ff(ss[2]); \
|
||||
ss[3] ^= ss[2]; \
|
||||
k[v(56, (8 * (i)) + 11)] = ff(ss[3]); \
|
||||
ss[4] ^= ls_box(ss[3], 0); \
|
||||
k[v(56, (8 * (i)) + 12)] = ff(ss[4]); \
|
||||
ss[5] ^= ss[4]; \
|
||||
k[v(56, (8 * (i)) + 13)] = ff(ss[5]); \
|
||||
ss[6] ^= ss[5]; \
|
||||
k[v(56, (8 * (i)) + 14)] = ff(ss[6]); \
|
||||
ss[7] ^= ss[6]; \
|
||||
k[v(56, (8 * (i)) + 15)] = ff(ss[7]); \
|
||||
}
|
||||
|
||||
#define kd8(k, i) \
|
||||
{ \
|
||||
ss[8] = ls_box(ss[7], 3) ^ t_use(r, c)[i]; \
|
||||
ss[0] ^= ss[8]; \
|
||||
ss[8] = ff(ss[8]); \
|
||||
k[v(56, (8 * (i)) + 8)] = ss[8] ^= k[v(56, (8 * (i)))]; \
|
||||
ss[1] ^= ss[0]; \
|
||||
k[v(56, (8 * (i)) + 9)] = ss[8] ^= k[v(56, (8 * (i)) + 1)]; \
|
||||
ss[2] ^= ss[1]; \
|
||||
k[v(56, (8 * (i)) + 10)] = ss[8] ^= k[v(56, (8 * (i)) + 2)]; \
|
||||
ss[3] ^= ss[2]; \
|
||||
k[v(56, (8 * (i)) + 11)] = ss[8] ^= k[v(56, (8 * (i)) + 3)]; \
|
||||
ss[8] = ls_box(ss[3], 0); \
|
||||
ss[4] ^= ss[8]; \
|
||||
ss[8] = ff(ss[8]); \
|
||||
k[v(56, (8 * (i)) + 12)] = ss[8] ^= k[v(56, (8 * (i)) + 4)]; \
|
||||
ss[5] ^= ss[4]; \
|
||||
k[v(56, (8 * (i)) + 13)] = ss[8] ^= k[v(56, (8 * (i)) + 5)]; \
|
||||
ss[6] ^= ss[5]; \
|
||||
k[v(56, (8 * (i)) + 14)] = ss[8] ^= k[v(56, (8 * (i)) + 6)]; \
|
||||
ss[7] ^= ss[6]; \
|
||||
k[v(56, (8 * (i)) + 15)] = ss[8] ^= k[v(56, (8 * (i)) + 7)]; \
|
||||
}
|
||||
|
||||
#define kdl8(k, i) \
|
||||
{ \
|
||||
ss[0] ^= ls_box(ss[7], 3) ^ t_use(r, c)[i]; \
|
||||
k[v(56, (8 * (i)) + 8)] = ss[0]; \
|
||||
ss[1] ^= ss[0]; \
|
||||
k[v(56, (8 * (i)) + 9)] = ss[1]; \
|
||||
ss[2] ^= ss[1]; \
|
||||
k[v(56, (8 * (i)) + 10)] = ss[2]; \
|
||||
ss[3] ^= ss[2]; \
|
||||
k[v(56, (8 * (i)) + 11)] = ss[3]; \
|
||||
}
|
||||
|
||||
AES_RETURN aes_xi(decrypt_key256)(const unsigned char* key, aes_decrypt_ctx cx[1]) {
|
||||
uint32_t ss[9];
|
||||
#if defined(d_vars)
|
||||
d_vars;
|
||||
#endif
|
||||
|
||||
cx->ks[v(56, (0))] = ss[0] = word_in(key, 0);
|
||||
cx->ks[v(56, (1))] = ss[1] = word_in(key, 1);
|
||||
cx->ks[v(56, (2))] = ss[2] = word_in(key, 2);
|
||||
cx->ks[v(56, (3))] = ss[3] = word_in(key, 3);
|
||||
|
||||
#ifdef DEC_KS_UNROLL
|
||||
ss[4] = word_in(key, 4);
|
||||
ss[5] = word_in(key, 5);
|
||||
ss[6] = word_in(key, 6);
|
||||
ss[7] = word_in(key, 7);
|
||||
cx->ks[v(56, (4))] = ff(ss[4]);
|
||||
cx->ks[v(56, (5))] = ff(ss[5]);
|
||||
cx->ks[v(56, (6))] = ff(ss[6]);
|
||||
cx->ks[v(56, (7))] = ff(ss[7]);
|
||||
kdf8(cx->ks, 0);
|
||||
kd8(cx->ks, 1);
|
||||
kd8(cx->ks, 2);
|
||||
kd8(cx->ks, 3);
|
||||
kd8(cx->ks, 4);
|
||||
kd8(cx->ks, 5);
|
||||
kdl8(cx->ks, 6);
|
||||
#else
|
||||
cx->ks[v(56, (4))] = ss[4] = word_in(key, 4);
|
||||
cx->ks[v(56, (5))] = ss[5] = word_in(key, 5);
|
||||
cx->ks[v(56, (6))] = ss[6] = word_in(key, 6);
|
||||
cx->ks[v(56, (7))] = ss[7] = word_in(key, 7);
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for(i = 0; i < 6; ++i) k8e(cx->ks, i);
|
||||
k8ef(cx->ks, 6);
|
||||
#if !(DEC_ROUND == NO_TABLES)
|
||||
for(i = N_COLS; i < 14 * N_COLS; ++i) cx->ks[i] = inv_mcol(cx->ks[i]);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
cx->inf.l = 0;
|
||||
cx->inf.b[0] = 14 * AES_BLOCK_SIZE;
|
||||
|
||||
#ifdef USE_VIA_ACE_IF_PRESENT
|
||||
if(VIA_ACE_AVAILABLE) cx->inf.b[1] = 0xff;
|
||||
#endif
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(AES_VAR)
|
||||
|
||||
AES_RETURN aes_encrypt_key(const unsigned char* key, int key_len, aes_encrypt_ctx cx[1]) {
|
||||
switch(key_len) {
|
||||
case 16:
|
||||
case 128:
|
||||
return aes_encrypt_key128(key, cx);
|
||||
case 24:
|
||||
case 192:
|
||||
return aes_encrypt_key192(key, cx);
|
||||
case 32:
|
||||
case 256:
|
||||
return aes_encrypt_key256(key, cx);
|
||||
default:
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
AES_RETURN aes_decrypt_key(const unsigned char* key, int key_len, aes_decrypt_ctx cx[1]) {
|
||||
switch(key_len) {
|
||||
case 16:
|
||||
case 128:
|
||||
return aes_decrypt_key128(key, cx);
|
||||
case 24:
|
||||
case 192:
|
||||
return aes_decrypt_key192(key, cx);
|
||||
case 32:
|
||||
case 256:
|
||||
return aes_decrypt_key256(key, cx);
|
||||
default:
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,793 @@
|
||||
/*
|
||||
---------------------------------------------------------------------------
|
||||
Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved.
|
||||
|
||||
The redistribution and use of this software (with or without changes)
|
||||
is allowed without the payment of fees or royalties provided that:
|
||||
|
||||
source code distributions include the above copyright notice, this
|
||||
list of conditions and the following disclaimer;
|
||||
|
||||
binary distributions include the above copyright notice, this list
|
||||
of conditions and the following disclaimer in their documentation.
|
||||
|
||||
This software is provided 'as is' with no explicit or implied warranties
|
||||
in respect of its operation, including, but not limited to, correctness
|
||||
and fitness for purpose.
|
||||
---------------------------------------------------------------------------
|
||||
Issue Date: 20/12/2007
|
||||
|
||||
This file contains the compilation options for AES (Rijndael) and code
|
||||
that is common across encryption, key scheduling and table generation.
|
||||
|
||||
OPERATION
|
||||
|
||||
These source code files implement the AES algorithm Rijndael designed by
|
||||
Joan Daemen and Vincent Rijmen. This version is designed for the standard
|
||||
block size of 16 bytes and for key sizes of 128, 192 and 256 bits (16, 24
|
||||
and 32 bytes).
|
||||
|
||||
This version is designed for flexibility and speed using operations on
|
||||
32-bit words rather than operations on bytes. It can be compiled with
|
||||
either big or little endian internal byte order but is faster when the
|
||||
native byte order for the processor is used.
|
||||
|
||||
THE CIPHER INTERFACE
|
||||
|
||||
The cipher interface is implemented as an array of bytes in which lower
|
||||
AES bit sequence indexes map to higher numeric significance within bytes.
|
||||
|
||||
uint8_t (an unsigned 8-bit type)
|
||||
uint32_t (an unsigned 32-bit type)
|
||||
struct aes_encrypt_ctx (structure for the cipher encryption context)
|
||||
struct aes_decrypt_ctx (structure for the cipher decryption context)
|
||||
AES_RETURN the function return type
|
||||
|
||||
C subroutine calls:
|
||||
|
||||
AES_RETURN aes_encrypt_key128(const unsigned char *key, aes_encrypt_ctx cx[1]);
|
||||
AES_RETURN aes_encrypt_key192(const unsigned char *key, aes_encrypt_ctx cx[1]);
|
||||
AES_RETURN aes_encrypt_key256(const unsigned char *key, aes_encrypt_ctx cx[1]);
|
||||
AES_RETURN aes_encrypt(const unsigned char *in, unsigned char *out,
|
||||
const aes_encrypt_ctx cx[1]);
|
||||
|
||||
AES_RETURN aes_decrypt_key128(const unsigned char *key, aes_decrypt_ctx cx[1]);
|
||||
AES_RETURN aes_decrypt_key192(const unsigned char *key, aes_decrypt_ctx cx[1]);
|
||||
AES_RETURN aes_decrypt_key256(const unsigned char *key, aes_decrypt_ctx cx[1]);
|
||||
AES_RETURN aes_decrypt(const unsigned char *in, unsigned char *out,
|
||||
const aes_decrypt_ctx cx[1]);
|
||||
|
||||
IMPORTANT NOTE: If you are using this C interface with dynamic tables make sure that
|
||||
you call aes_init() before AES is used so that the tables are initialised.
|
||||
|
||||
C++ aes class subroutines:
|
||||
|
||||
Class AESencrypt for encryption
|
||||
|
||||
Constructors:
|
||||
AESencrypt(void)
|
||||
AESencrypt(const unsigned char *key) - 128 bit key
|
||||
Members:
|
||||
AES_RETURN key128(const unsigned char *key)
|
||||
AES_RETURN key192(const unsigned char *key)
|
||||
AES_RETURN key256(const unsigned char *key)
|
||||
AES_RETURN encrypt(const unsigned char *in, unsigned char *out) const
|
||||
|
||||
Class AESdecrypt for encryption
|
||||
Constructors:
|
||||
AESdecrypt(void)
|
||||
AESdecrypt(const unsigned char *key) - 128 bit key
|
||||
Members:
|
||||
AES_RETURN key128(const unsigned char *key)
|
||||
AES_RETURN key192(const unsigned char *key)
|
||||
AES_RETURN key256(const unsigned char *key)
|
||||
AES_RETURN decrypt(const unsigned char *in, unsigned char *out) const
|
||||
*/
|
||||
|
||||
#if !defined(_AESOPT_H)
|
||||
#define _AESOPT_H
|
||||
|
||||
#if defined(__cplusplus)
|
||||
#include "aescpp.h"
|
||||
#else
|
||||
#include "aes.h"
|
||||
#endif
|
||||
|
||||
/* PLATFORM SPECIFIC INCLUDES */
|
||||
|
||||
#define IS_BIG_ENDIAN 4321
|
||||
#define IS_LITTLE_ENDIAN 1234
|
||||
#define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
|
||||
|
||||
/* CONFIGURATION - THE USE OF DEFINES
|
||||
|
||||
Later in this section there are a number of defines that control the
|
||||
operation of the code. In each section, the purpose of each define is
|
||||
explained so that the relevant form can be included or excluded by
|
||||
setting either 1's or 0's respectively on the branches of the related
|
||||
#if clauses. The following local defines should not be changed.
|
||||
*/
|
||||
|
||||
#define ENCRYPTION_IN_C 1
|
||||
#define DECRYPTION_IN_C 2
|
||||
#define ENC_KEYING_IN_C 4
|
||||
#define DEC_KEYING_IN_C 8
|
||||
|
||||
#define NO_TABLES 0
|
||||
#define ONE_TABLE 1
|
||||
#define FOUR_TABLES 4
|
||||
#define NONE 0
|
||||
#define PARTIAL 1
|
||||
#define FULL 2
|
||||
|
||||
/* --- START OF USER CONFIGURED OPTIONS --- */
|
||||
|
||||
/* 1. BYTE ORDER WITHIN 32 BIT WORDS
|
||||
|
||||
The fundamental data processing units in Rijndael are 8-bit bytes. The
|
||||
input, output and key input are all enumerated arrays of bytes in which
|
||||
bytes are numbered starting at zero and increasing to one less than the
|
||||
number of bytes in the array in question. This enumeration is only used
|
||||
for naming bytes and does not imply any adjacency or order relationship
|
||||
from one byte to another. When these inputs and outputs are considered
|
||||
as bit sequences, bits 8*n to 8*n+7 of the bit sequence are mapped to
|
||||
byte[n] with bit 8n+i in the sequence mapped to bit 7-i within the byte.
|
||||
In this implementation bits are numbered from 0 to 7 starting at the
|
||||
numerically least significant end of each byte (bit n represents 2^n).
|
||||
|
||||
However, Rijndael can be implemented more efficiently using 32-bit
|
||||
words by packing bytes into words so that bytes 4*n to 4*n+3 are placed
|
||||
into word[n]. While in principle these bytes can be assembled into words
|
||||
in any positions, this implementation only supports the two formats in
|
||||
which bytes in adjacent positions within words also have adjacent byte
|
||||
numbers. This order is called big-endian if the lowest numbered bytes
|
||||
in words have the highest numeric significance and little-endian if the
|
||||
opposite applies.
|
||||
|
||||
This code can work in either order irrespective of the order used by the
|
||||
machine on which it runs. Normally the internal byte order will be set
|
||||
to the order of the processor on which the code is to be run but this
|
||||
define can be used to reverse this in special situations
|
||||
|
||||
WARNING: Assembler code versions rely on PLATFORM_BYTE_ORDER being set.
|
||||
This define will hence be redefined later (in section 4) if necessary
|
||||
*/
|
||||
|
||||
#if 1
|
||||
#define ALGORITHM_BYTE_ORDER PLATFORM_BYTE_ORDER
|
||||
#elif 0
|
||||
#define ALGORITHM_BYTE_ORDER IS_LITTLE_ENDIAN
|
||||
#elif 0
|
||||
#define ALGORITHM_BYTE_ORDER IS_BIG_ENDIAN
|
||||
#else
|
||||
#error The algorithm byte order is not defined
|
||||
#endif
|
||||
|
||||
/* 2. Intel AES AND VIA ACE SUPPORT */
|
||||
|
||||
#if defined(__GNUC__) && defined(__i386__) && !defined(__BEOS__) || \
|
||||
defined(_WIN32) && defined(_M_IX86) && \
|
||||
!(defined(_WIN64) || defined(_WIN32_WCE) || defined(_MSC_VER) && (_MSC_VER <= 800))
|
||||
#define VIA_ACE_POSSIBLE
|
||||
#endif
|
||||
|
||||
/* AESNI is supported by all Windows x64 compilers, but for Linux/GCC
|
||||
we have to test for SSE 2, SSE 3, and AES to before enabling it; */
|
||||
#if !defined(INTEL_AES_POSSIBLE)
|
||||
#if defined(_WIN64) && defined(_MSC_VER) || defined(__GNUC__) && defined(__x86_64__) && \
|
||||
defined(__SSE2__) && defined(__SSE3__) && \
|
||||
defined(__AES__)
|
||||
#define INTEL_AES_POSSIBLE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Define this option if support for the Intel AESNI is required
|
||||
If USE_INTEL_AES_IF_PRESENT is defined then AESNI will be used
|
||||
if it is detected (both present and enabled).
|
||||
|
||||
AESNI uses a decryption key schedule with the first decryption
|
||||
round key at the high end of the key scedule with the following
|
||||
round keys at lower positions in memory. So AES_REV_DKS must NOT
|
||||
be defined when AESNI will be used. Although it is unlikely that
|
||||
assembler code will be used with an AESNI build, if it is then
|
||||
AES_REV_DKS must NOT be defined when the assembler files are
|
||||
built (the definition of USE_INTEL_AES_IF_PRESENT in the assembler
|
||||
code files must match that here if they are used).
|
||||
*/
|
||||
|
||||
#if 0 && defined(INTEL_AES_POSSIBLE) && !defined(USE_INTEL_AES_IF_PRESENT)
|
||||
#define USE_INTEL_AES_IF_PRESENT
|
||||
#endif
|
||||
|
||||
/* Define this option if support for the VIA ACE is required. This uses
|
||||
inline assembler instructions and is only implemented for the Microsoft,
|
||||
Intel and GCC compilers. If VIA ACE is known to be present, then defining
|
||||
ASSUME_VIA_ACE_PRESENT will remove the ordinary encryption/decryption
|
||||
code. If USE_VIA_ACE_IF_PRESENT is defined then VIA ACE will be used if
|
||||
it is detected (both present and enabled) but the normal AES code will
|
||||
also be present.
|
||||
|
||||
When VIA ACE is to be used, all AES encryption contexts MUST be 16 byte
|
||||
aligned; other input/output buffers do not need to be 16 byte aligned
|
||||
but there are very large performance gains if this can be arranged.
|
||||
VIA ACE also requires the decryption key schedule to be in reverse
|
||||
order (which later checks below ensure).
|
||||
|
||||
AES_REV_DKS must be set for assembler code used with a VIA ACE build
|
||||
*/
|
||||
|
||||
#if 0 && defined(VIA_ACE_POSSIBLE) && !defined(USE_VIA_ACE_IF_PRESENT)
|
||||
#define USE_VIA_ACE_IF_PRESENT
|
||||
#endif
|
||||
|
||||
#if 0 && defined(VIA_ACE_POSSIBLE) && !defined(ASSUME_VIA_ACE_PRESENT)
|
||||
#define ASSUME_VIA_ACE_PRESENT
|
||||
#endif
|
||||
|
||||
/* 3. ASSEMBLER SUPPORT
|
||||
|
||||
This define (which can be on the command line) enables the use of the
|
||||
assembler code routines for encryption, decryption and key scheduling
|
||||
as follows:
|
||||
|
||||
ASM_X86_V1C uses the assembler (aes_x86_v1.asm) with large tables for
|
||||
encryption and decryption and but with key scheduling in C
|
||||
ASM_X86_V2 uses assembler (aes_x86_v2.asm) with compressed tables for
|
||||
encryption, decryption and key scheduling
|
||||
ASM_X86_V2C uses assembler (aes_x86_v2.asm) with compressed tables for
|
||||
encryption and decryption and but with key scheduling in C
|
||||
ASM_AMD64_C uses assembler (aes_amd64.asm) with compressed tables for
|
||||
encryption and decryption and but with key scheduling in C
|
||||
|
||||
Change one 'if 0' below to 'if 1' to select the version or define
|
||||
as a compilation option.
|
||||
*/
|
||||
|
||||
#if 0 && !defined(ASM_X86_V1C)
|
||||
#define ASM_X86_V1C
|
||||
#elif 0 && !defined(ASM_X86_V2)
|
||||
#define ASM_X86_V2
|
||||
#elif 0 && !defined(ASM_X86_V2C)
|
||||
#define ASM_X86_V2C
|
||||
#elif 0 && !defined(ASM_AMD64_C)
|
||||
#define ASM_AMD64_C
|
||||
#endif
|
||||
|
||||
#if defined(__i386) || defined(_M_IX86)
|
||||
#define A32_
|
||||
#elif defined(__x86_64__) || defined(_M_X64)
|
||||
#define A64_
|
||||
#endif
|
||||
|
||||
#if(defined(ASM_X86_V1C) || defined(ASM_X86_V2) || defined(ASM_X86_V2C)) && !defined(A32_) || \
|
||||
defined(ASM_AMD64_C) && !defined(A64_)
|
||||
#error Assembler code is only available for x86 and AMD64 systems
|
||||
#endif
|
||||
|
||||
/* 4. FAST INPUT/OUTPUT OPERATIONS.
|
||||
|
||||
On some machines it is possible to improve speed by transferring the
|
||||
bytes in the input and output arrays to and from the internal 32-bit
|
||||
variables by addressing these arrays as if they are arrays of 32-bit
|
||||
words. On some machines this will always be possible but there may
|
||||
be a large performance penalty if the byte arrays are not aligned on
|
||||
the normal word boundaries. On other machines this technique will
|
||||
lead to memory access errors when such 32-bit word accesses are not
|
||||
properly aligned. The option SAFE_IO avoids such problems but will
|
||||
often be slower on those machines that support misaligned access
|
||||
(especially so if care is taken to align the input and output byte
|
||||
arrays on 32-bit word boundaries). If SAFE_IO is not defined it is
|
||||
assumed that access to byte arrays as if they are arrays of 32-bit
|
||||
words will not cause problems when such accesses are misaligned.
|
||||
*/
|
||||
#if 1 && !defined(_MSC_VER)
|
||||
#define SAFE_IO
|
||||
#endif
|
||||
|
||||
/* 5. LOOP UNROLLING
|
||||
|
||||
The code for encryption and decrytpion cycles through a number of rounds
|
||||
that can be implemented either in a loop or by expanding the code into a
|
||||
long sequence of instructions, the latter producing a larger program but
|
||||
one that will often be much faster. The latter is called loop unrolling.
|
||||
There are also potential speed advantages in expanding two iterations in
|
||||
a loop with half the number of iterations, which is called partial loop
|
||||
unrolling. The following options allow partial or full loop unrolling
|
||||
to be set independently for encryption and decryption
|
||||
*/
|
||||
#if 1
|
||||
#define ENC_UNROLL FULL
|
||||
#elif 0
|
||||
#define ENC_UNROLL PARTIAL
|
||||
#else
|
||||
#define ENC_UNROLL NONE
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
#define DEC_UNROLL FULL
|
||||
#elif 0
|
||||
#define DEC_UNROLL PARTIAL
|
||||
#else
|
||||
#define DEC_UNROLL NONE
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
#define ENC_KS_UNROLL
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
#define DEC_KS_UNROLL
|
||||
#endif
|
||||
|
||||
/* 6. FAST FINITE FIELD OPERATIONS
|
||||
|
||||
If this section is included, tables are used to provide faster finite
|
||||
field arithmetic (this has no effect if STATIC_TABLES is defined).
|
||||
*/
|
||||
#if 1
|
||||
#define FF_TABLES
|
||||
#endif
|
||||
|
||||
/* 7. INTERNAL STATE VARIABLE FORMAT
|
||||
|
||||
The internal state of Rijndael is stored in a number of local 32-bit
|
||||
word varaibles which can be defined either as an array or as individual
|
||||
names variables. Include this section if you want to store these local
|
||||
varaibles in arrays. Otherwise individual local variables will be used.
|
||||
*/
|
||||
#if 1
|
||||
#define ARRAYS
|
||||
#endif
|
||||
|
||||
/* 8. FIXED OR DYNAMIC TABLES
|
||||
|
||||
When this section is included the tables used by the code are compiled
|
||||
statically into the binary file. Otherwise the subroutine aes_init()
|
||||
must be called to compute them before the code is first used.
|
||||
*/
|
||||
#if 1 && !(defined(_MSC_VER) && (_MSC_VER <= 800))
|
||||
#define STATIC_TABLES
|
||||
#endif
|
||||
|
||||
/* 9. MASKING OR CASTING FROM LONGER VALUES TO BYTES
|
||||
|
||||
In some systems it is better to mask longer values to extract bytes
|
||||
rather than using a cast. This option allows this choice.
|
||||
*/
|
||||
#if 0
|
||||
#define to_byte(x) ((uint8_t)(x))
|
||||
#else
|
||||
#define to_byte(x) ((x)&0xff)
|
||||
#endif
|
||||
|
||||
/* 10. TABLE ALIGNMENT
|
||||
|
||||
On some sytsems speed will be improved by aligning the AES large lookup
|
||||
tables on particular boundaries. This define should be set to a power of
|
||||
two giving the desired alignment. It can be left undefined if alignment
|
||||
is not needed. This option is specific to the Microsft VC++ compiler -
|
||||
it seems to sometimes cause trouble for the VC++ version 6 compiler.
|
||||
*/
|
||||
|
||||
#if 1 && defined(_MSC_VER) && (_MSC_VER >= 1300)
|
||||
#define TABLE_ALIGN 32
|
||||
#endif
|
||||
|
||||
/* 11. REDUCE CODE AND TABLE SIZE
|
||||
|
||||
This replaces some expanded macros with function calls if AES_ASM_V2 or
|
||||
AES_ASM_V2C are defined
|
||||
*/
|
||||
|
||||
#if 1 && (defined(ASM_X86_V2) || defined(ASM_X86_V2C))
|
||||
#define REDUCE_CODE_SIZE
|
||||
#endif
|
||||
|
||||
/* 12. TABLE OPTIONS
|
||||
|
||||
This cipher proceeds by repeating in a number of cycles known as 'rounds'
|
||||
which are implemented by a round function which can optionally be speeded
|
||||
up using tables. The basic tables are each 256 32-bit words, with either
|
||||
one or four tables being required for each round function depending on
|
||||
how much speed is required. The encryption and decryption round functions
|
||||
are different and the last encryption and decrytpion round functions are
|
||||
different again making four different round functions in all.
|
||||
|
||||
This means that:
|
||||
1. Normal encryption and decryption rounds can each use either 0, 1
|
||||
or 4 tables and table spaces of 0, 1024 or 4096 bytes each.
|
||||
2. The last encryption and decryption rounds can also use either 0, 1
|
||||
or 4 tables and table spaces of 0, 1024 or 4096 bytes each.
|
||||
|
||||
Include or exclude the appropriate definitions below to set the number
|
||||
of tables used by this implementation.
|
||||
*/
|
||||
|
||||
#if 1 /* set tables for the normal encryption round */
|
||||
#define ENC_ROUND FOUR_TABLES
|
||||
#elif 0
|
||||
#define ENC_ROUND ONE_TABLE
|
||||
#else
|
||||
#define ENC_ROUND NO_TABLES
|
||||
#endif
|
||||
|
||||
#if 1 /* set tables for the last encryption round */
|
||||
#define LAST_ENC_ROUND FOUR_TABLES
|
||||
#elif 0
|
||||
#define LAST_ENC_ROUND ONE_TABLE
|
||||
#else
|
||||
#define LAST_ENC_ROUND NO_TABLES
|
||||
#endif
|
||||
|
||||
#if 1 /* set tables for the normal decryption round */
|
||||
#define DEC_ROUND FOUR_TABLES
|
||||
#elif 0
|
||||
#define DEC_ROUND ONE_TABLE
|
||||
#else
|
||||
#define DEC_ROUND NO_TABLES
|
||||
#endif
|
||||
|
||||
#if 1 /* set tables for the last decryption round */
|
||||
#define LAST_DEC_ROUND FOUR_TABLES
|
||||
#elif 0
|
||||
#define LAST_DEC_ROUND ONE_TABLE
|
||||
#else
|
||||
#define LAST_DEC_ROUND NO_TABLES
|
||||
#endif
|
||||
|
||||
/* The decryption key schedule can be speeded up with tables in the same
|
||||
way that the round functions can. Include or exclude the following
|
||||
defines to set this requirement.
|
||||
*/
|
||||
#if 1
|
||||
#define KEY_SCHED FOUR_TABLES
|
||||
#elif 0
|
||||
#define KEY_SCHED ONE_TABLE
|
||||
#else
|
||||
#define KEY_SCHED NO_TABLES
|
||||
#endif
|
||||
|
||||
/* ---- END OF USER CONFIGURED OPTIONS ---- */
|
||||
|
||||
/* VIA ACE support is only available for VC++ and GCC */
|
||||
|
||||
#if !defined(_MSC_VER) && !defined(__GNUC__)
|
||||
#if defined(ASSUME_VIA_ACE_PRESENT)
|
||||
#undef ASSUME_VIA_ACE_PRESENT
|
||||
#endif
|
||||
#if defined(USE_VIA_ACE_IF_PRESENT)
|
||||
#undef USE_VIA_ACE_IF_PRESENT
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(ASSUME_VIA_ACE_PRESENT) && !defined(USE_VIA_ACE_IF_PRESENT)
|
||||
#define USE_VIA_ACE_IF_PRESENT
|
||||
#endif
|
||||
|
||||
/* define to reverse decryption key schedule */
|
||||
#if 1 || defined(USE_VIA_ACE_IF_PRESENT) && !defined(AES_REV_DKS)
|
||||
#define AES_REV_DKS
|
||||
#endif
|
||||
|
||||
/* Intel AESNI uses a decryption key schedule in the encryption order */
|
||||
#if defined(USE_INTEL_AES_IF_PRESENT) && defined(AES_REV_DKS)
|
||||
#undef AES_REV_DKS
|
||||
#endif
|
||||
|
||||
/* Assembler support requires the use of platform byte order */
|
||||
|
||||
#if(defined(ASM_X86_V1C) || defined(ASM_X86_V2C) || defined(ASM_AMD64_C)) && \
|
||||
(ALGORITHM_BYTE_ORDER != PLATFORM_BYTE_ORDER)
|
||||
#undef ALGORITHM_BYTE_ORDER
|
||||
#define ALGORITHM_BYTE_ORDER PLATFORM_BYTE_ORDER
|
||||
#endif
|
||||
|
||||
/* In this implementation the columns of the state array are each held in
|
||||
32-bit words. The state array can be held in various ways: in an array
|
||||
of words, in a number of individual word variables or in a number of
|
||||
processor registers. The following define maps a variable name x and
|
||||
a column number c to the way the state array variable is to be held.
|
||||
The first define below maps the state into an array x[c] whereas the
|
||||
second form maps the state into a number of individual variables x0,
|
||||
x1, etc. Another form could map individual state colums to machine
|
||||
register names.
|
||||
*/
|
||||
|
||||
#if defined(ARRAYS)
|
||||
#define s(x, c) x[c]
|
||||
#else
|
||||
#define s(x, c) x##c
|
||||
#endif
|
||||
|
||||
/* This implementation provides subroutines for encryption, decryption
|
||||
and for setting the three key lengths (separately) for encryption
|
||||
and decryption. Since not all functions are needed, masks are set
|
||||
up here to determine which will be implemented in C
|
||||
*/
|
||||
|
||||
#if !defined(AES_ENCRYPT)
|
||||
#define EFUNCS_IN_C 0
|
||||
#elif defined(ASSUME_VIA_ACE_PRESENT) || defined(ASM_X86_V1C) || defined(ASM_X86_V2C) || \
|
||||
defined(ASM_AMD64_C)
|
||||
#define EFUNCS_IN_C ENC_KEYING_IN_C
|
||||
#elif !defined(ASM_X86_V2)
|
||||
#define EFUNCS_IN_C (ENCRYPTION_IN_C | ENC_KEYING_IN_C)
|
||||
#else
|
||||
#define EFUNCS_IN_C 0
|
||||
#endif
|
||||
|
||||
#if !defined(AES_DECRYPT)
|
||||
#define DFUNCS_IN_C 0
|
||||
#elif defined(ASSUME_VIA_ACE_PRESENT) || defined(ASM_X86_V1C) || defined(ASM_X86_V2C) || \
|
||||
defined(ASM_AMD64_C)
|
||||
#define DFUNCS_IN_C DEC_KEYING_IN_C
|
||||
#elif !defined(ASM_X86_V2)
|
||||
#define DFUNCS_IN_C (DECRYPTION_IN_C | DEC_KEYING_IN_C)
|
||||
#else
|
||||
#define DFUNCS_IN_C 0
|
||||
#endif
|
||||
|
||||
#define FUNCS_IN_C (EFUNCS_IN_C | DFUNCS_IN_C)
|
||||
|
||||
/* END OF CONFIGURATION OPTIONS */
|
||||
|
||||
#define RC_LENGTH (5 * (AES_BLOCK_SIZE / 4 - 2))
|
||||
|
||||
/* Disable or report errors on some combinations of options */
|
||||
|
||||
#if ENC_ROUND == NO_TABLES && LAST_ENC_ROUND != NO_TABLES
|
||||
#undef LAST_ENC_ROUND
|
||||
#define LAST_ENC_ROUND NO_TABLES
|
||||
#elif ENC_ROUND == ONE_TABLE && LAST_ENC_ROUND == FOUR_TABLES
|
||||
#undef LAST_ENC_ROUND
|
||||
#define LAST_ENC_ROUND ONE_TABLE
|
||||
#endif
|
||||
|
||||
#if ENC_ROUND == NO_TABLES && ENC_UNROLL != NONE
|
||||
#undef ENC_UNROLL
|
||||
#define ENC_UNROLL NONE
|
||||
#endif
|
||||
|
||||
#if DEC_ROUND == NO_TABLES && LAST_DEC_ROUND != NO_TABLES
|
||||
#undef LAST_DEC_ROUND
|
||||
#define LAST_DEC_ROUND NO_TABLES
|
||||
#elif DEC_ROUND == ONE_TABLE && LAST_DEC_ROUND == FOUR_TABLES
|
||||
#undef LAST_DEC_ROUND
|
||||
#define LAST_DEC_ROUND ONE_TABLE
|
||||
#endif
|
||||
|
||||
#if DEC_ROUND == NO_TABLES && DEC_UNROLL != NONE
|
||||
#undef DEC_UNROLL
|
||||
#define DEC_UNROLL NONE
|
||||
#endif
|
||||
|
||||
#if defined(bswap32)
|
||||
#define aes_sw32 bswap32
|
||||
#elif defined(bswap_32)
|
||||
#define aes_sw32 bswap_32
|
||||
#else
|
||||
#define brot(x, n) (((uint32_t)(x) << n) | ((uint32_t)(x) >> (32 - n)))
|
||||
#define aes_sw32(x) ((brot((x), 8) & 0x00ff00ff) | (brot((x), 24) & 0xff00ff00))
|
||||
#endif
|
||||
|
||||
/* upr(x,n): rotates bytes within words by n positions, moving bytes to
|
||||
higher index positions with wrap around into low positions
|
||||
ups(x,n): moves bytes by n positions to higher index positions in
|
||||
words but without wrap around
|
||||
bval(x,n): extracts a byte from a word
|
||||
|
||||
WARNING: The definitions given here are intended only for use with
|
||||
unsigned variables and with shift counts that are compile
|
||||
time constants
|
||||
*/
|
||||
|
||||
#if(ALGORITHM_BYTE_ORDER == IS_LITTLE_ENDIAN)
|
||||
#define upr(x, n) (((uint32_t)(x) << (8 * (n))) | ((uint32_t)(x) >> (32 - 8 * (n))))
|
||||
#define ups(x, n) ((uint32_t)(x) << (8 * (n)))
|
||||
#define bval(x, n) to_byte((x) >> (8 * (n)))
|
||||
#define bytes2word(b0, b1, b2, b3) \
|
||||
(((uint32_t)(b3) << 24) | ((uint32_t)(b2) << 16) | ((uint32_t)(b1) << 8) | (b0))
|
||||
#endif
|
||||
|
||||
#if(ALGORITHM_BYTE_ORDER == IS_BIG_ENDIAN)
|
||||
#define upr(x, n) (((uint32_t)(x) >> (8 * (n))) | ((uint32_t)(x) << (32 - 8 * (n))))
|
||||
#define ups(x, n) ((uint32_t)(x) >> (8 * (n)))
|
||||
#define bval(x, n) to_byte((x) >> (24 - 8 * (n)))
|
||||
#define bytes2word(b0, b1, b2, b3) \
|
||||
(((uint32_t)(b0) << 24) | ((uint32_t)(b1) << 16) | ((uint32_t)(b2) << 8) | (b3))
|
||||
#endif
|
||||
|
||||
#if defined(SAFE_IO)
|
||||
#define word_in(x, c) \
|
||||
bytes2word( \
|
||||
((const uint8_t*)(x) + 4 * c)[0], \
|
||||
((const uint8_t*)(x) + 4 * c)[1], \
|
||||
((const uint8_t*)(x) + 4 * c)[2], \
|
||||
((const uint8_t*)(x) + 4 * c)[3])
|
||||
#define word_out(x, c, v) \
|
||||
{ \
|
||||
((uint8_t*)(x) + 4 * c)[0] = bval(v, 0); \
|
||||
((uint8_t*)(x) + 4 * c)[1] = bval(v, 1); \
|
||||
((uint8_t*)(x) + 4 * c)[2] = bval(v, 2); \
|
||||
((uint8_t*)(x) + 4 * c)[3] = bval(v, 3); \
|
||||
}
|
||||
#elif(ALGORITHM_BYTE_ORDER == PLATFORM_BYTE_ORDER)
|
||||
#define word_in(x, c) (*((uint32_t*)(x) + (c)))
|
||||
#define word_out(x, c, v) (*((uint32_t*)(x) + (c)) = (v))
|
||||
#else
|
||||
#define word_in(x, c) aes_sw32(*((uint32_t*)(x) + (c)))
|
||||
#define word_out(x, c, v) (*((uint32_t*)(x) + (c)) = aes_sw32(v))
|
||||
#endif
|
||||
|
||||
/* the finite field modular polynomial and elements */
|
||||
|
||||
#define WPOLY 0x011b
|
||||
#define BPOLY 0x1b
|
||||
|
||||
/* multiply four bytes in GF(2^8) by 'x' {02} in parallel */
|
||||
|
||||
#define gf_c1 0x80808080
|
||||
#define gf_c2 0x7f7f7f7f
|
||||
#define gf_mulx(x) ((((x)&gf_c2) << 1) ^ ((((x)&gf_c1) >> 7) * BPOLY))
|
||||
|
||||
/* The following defines provide alternative definitions of gf_mulx that might
|
||||
give improved performance if a fast 32-bit multiply is not available. Note
|
||||
that a temporary variable u needs to be defined where gf_mulx is used.
|
||||
|
||||
#define gf_mulx(x) (u = (x) & gf_c1, u |= (u >> 1), ((x) & gf_c2) << 1) ^ ((u >> 3) | (u >> 6))
|
||||
#define gf_c4 (0x01010101 * BPOLY)
|
||||
#define gf_mulx(x) (u = (x) & gf_c1, ((x) & gf_c2) << 1) ^ ((u - (u >> 7)) & gf_c4)
|
||||
*/
|
||||
|
||||
/* Work out which tables are needed for the different options */
|
||||
|
||||
#if defined(ASM_X86_V1C)
|
||||
#if defined(ENC_ROUND)
|
||||
#undef ENC_ROUND
|
||||
#endif
|
||||
#define ENC_ROUND FOUR_TABLES
|
||||
#if defined(LAST_ENC_ROUND)
|
||||
#undef LAST_ENC_ROUND
|
||||
#endif
|
||||
#define LAST_ENC_ROUND FOUR_TABLES
|
||||
#if defined(DEC_ROUND)
|
||||
#undef DEC_ROUND
|
||||
#endif
|
||||
#define DEC_ROUND FOUR_TABLES
|
||||
#if defined(LAST_DEC_ROUND)
|
||||
#undef LAST_DEC_ROUND
|
||||
#endif
|
||||
#define LAST_DEC_ROUND FOUR_TABLES
|
||||
#if defined(KEY_SCHED)
|
||||
#undef KEY_SCHED
|
||||
#define KEY_SCHED FOUR_TABLES
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if(FUNCS_IN_C & ENCRYPTION_IN_C) || defined(ASM_X86_V1C)
|
||||
#if ENC_ROUND == ONE_TABLE
|
||||
#define FT1_SET
|
||||
#elif ENC_ROUND == FOUR_TABLES
|
||||
#define FT4_SET
|
||||
#else
|
||||
#define SBX_SET
|
||||
#endif
|
||||
#if LAST_ENC_ROUND == ONE_TABLE
|
||||
#define FL1_SET
|
||||
#elif LAST_ENC_ROUND == FOUR_TABLES
|
||||
#define FL4_SET
|
||||
#elif !defined(SBX_SET)
|
||||
#define SBX_SET
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if(FUNCS_IN_C & DECRYPTION_IN_C) || defined(ASM_X86_V1C)
|
||||
#if DEC_ROUND == ONE_TABLE
|
||||
#define IT1_SET
|
||||
#elif DEC_ROUND == FOUR_TABLES
|
||||
#define IT4_SET
|
||||
#else
|
||||
#define ISB_SET
|
||||
#endif
|
||||
#if LAST_DEC_ROUND == ONE_TABLE
|
||||
#define IL1_SET
|
||||
#elif LAST_DEC_ROUND == FOUR_TABLES
|
||||
#define IL4_SET
|
||||
#elif !defined(ISB_SET)
|
||||
#define ISB_SET
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !(defined(REDUCE_CODE_SIZE) && (defined(ASM_X86_V2) || defined(ASM_X86_V2C)))
|
||||
#if((FUNCS_IN_C & ENC_KEYING_IN_C) || (FUNCS_IN_C & DEC_KEYING_IN_C))
|
||||
#if KEY_SCHED == ONE_TABLE
|
||||
#if !defined(FL1_SET) && !defined(FL4_SET)
|
||||
#define LS1_SET
|
||||
#endif
|
||||
#elif KEY_SCHED == FOUR_TABLES
|
||||
#if !defined(FL4_SET)
|
||||
#define LS4_SET
|
||||
#endif
|
||||
#elif !defined(SBX_SET)
|
||||
#define SBX_SET
|
||||
#endif
|
||||
#endif
|
||||
#if(FUNCS_IN_C & DEC_KEYING_IN_C)
|
||||
#if KEY_SCHED == ONE_TABLE
|
||||
#define IM1_SET
|
||||
#elif KEY_SCHED == FOUR_TABLES
|
||||
#define IM4_SET
|
||||
#elif !defined(SBX_SET)
|
||||
#define SBX_SET
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* generic definitions of Rijndael macros that use tables */
|
||||
|
||||
#define no_table(x, box, vf, rf, c) \
|
||||
bytes2word( \
|
||||
box[bval(vf(x, 0, c), rf(0, c))], \
|
||||
box[bval(vf(x, 1, c), rf(1, c))], \
|
||||
box[bval(vf(x, 2, c), rf(2, c))], \
|
||||
box[bval(vf(x, 3, c), rf(3, c))])
|
||||
|
||||
#define one_table(x, op, tab, vf, rf, c) \
|
||||
(tab[bval(vf(x, 0, c), rf(0, c))] ^ op(tab[bval(vf(x, 1, c), rf(1, c))], 1) ^ \
|
||||
op(tab[bval(vf(x, 2, c), rf(2, c))], 2) ^ op(tab[bval(vf(x, 3, c), rf(3, c))], 3))
|
||||
|
||||
#define four_tables(x, tab, vf, rf, c) \
|
||||
(tab[0][bval(vf(x, 0, c), rf(0, c))] ^ tab[1][bval(vf(x, 1, c), rf(1, c))] ^ \
|
||||
tab[2][bval(vf(x, 2, c), rf(2, c))] ^ tab[3][bval(vf(x, 3, c), rf(3, c))])
|
||||
|
||||
#define vf1(x, r, c) (x)
|
||||
#define rf1(r, c) (r)
|
||||
#define rf2(r, c) ((8 + r - c) & 3)
|
||||
|
||||
/* perform forward and inverse column mix operation on four bytes in long word x in */
|
||||
/* parallel. NOTE: x must be a simple variable, NOT an expression in these macros. */
|
||||
|
||||
#if !(defined(REDUCE_CODE_SIZE) && (defined(ASM_X86_V2) || defined(ASM_X86_V2C)))
|
||||
|
||||
#if defined(FM4_SET) /* not currently used */
|
||||
#define fwd_mcol(x) four_tables(x, t_use(f, m), vf1, rf1, 0)
|
||||
#elif defined(FM1_SET) /* not currently used */
|
||||
#define fwd_mcol(x) one_table(x, upr, t_use(f, m), vf1, rf1, 0)
|
||||
#else
|
||||
#define dec_fmvars uint32_t g2
|
||||
#define fwd_mcol(x) (g2 = gf_mulx(x), g2 ^ upr((x) ^ g2, 3) ^ upr((x), 2) ^ upr((x), 1))
|
||||
#endif
|
||||
|
||||
#if defined(IM4_SET)
|
||||
#define inv_mcol(x) four_tables(x, t_use(i, m), vf1, rf1, 0)
|
||||
#elif defined(IM1_SET)
|
||||
#define inv_mcol(x) one_table(x, upr, t_use(i, m), vf1, rf1, 0)
|
||||
#else
|
||||
#define dec_imvars uint32_t g2, g4, g9
|
||||
#define inv_mcol(x) \
|
||||
(g2 = gf_mulx(x), \
|
||||
g4 = gf_mulx(g2), \
|
||||
g9 = (x) ^ gf_mulx(g4), \
|
||||
g4 ^= g9, \
|
||||
(x) ^ g2 ^ g4 ^ upr(g2 ^ g9, 3) ^ upr(g4, 2) ^ upr(g9, 1))
|
||||
#endif
|
||||
|
||||
#if defined(FL4_SET)
|
||||
#define ls_box(x, c) four_tables(x, t_use(f, l), vf1, rf2, c)
|
||||
#elif defined(LS4_SET)
|
||||
#define ls_box(x, c) four_tables(x, t_use(l, s), vf1, rf2, c)
|
||||
#elif defined(FL1_SET)
|
||||
#define ls_box(x, c) one_table(x, upr, t_use(f, l), vf1, rf2, c)
|
||||
#elif defined(LS1_SET)
|
||||
#define ls_box(x, c) one_table(x, upr, t_use(l, s), vf1, rf2, c)
|
||||
#else
|
||||
#define ls_box(x, c) no_table(x, t_use(s, box), vf1, rf2, c)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(ASM_X86_V1C) && defined(AES_DECRYPT) && !defined(ISB_SET)
|
||||
#define ISB_SET
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
---------------------------------------------------------------------------
|
||||
Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved.
|
||||
|
||||
The redistribution and use of this software (with or without changes)
|
||||
is allowed without the payment of fees or royalties provided that:
|
||||
|
||||
source code distributions include the above copyright notice, this
|
||||
list of conditions and the following disclaimer;
|
||||
|
||||
binary distributions include the above copyright notice, this list
|
||||
of conditions and the following disclaimer in their documentation.
|
||||
|
||||
This software is provided 'as is' with no explicit or implied warranties
|
||||
in respect of its operation, including, but not limited to, correctness
|
||||
and fitness for purpose.
|
||||
---------------------------------------------------------------------------
|
||||
Issue Date: 20/12/2007
|
||||
*/
|
||||
|
||||
#define DO_TABLES
|
||||
|
||||
#include "aes.h"
|
||||
#include "aesopt.h"
|
||||
|
||||
#if defined(STATIC_TABLES)
|
||||
|
||||
#define sb_data(w) \
|
||||
{ \
|
||||
w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5), w(0x30), w(0x01), \
|
||||
w(0x67), w(0x2b), w(0xfe), w(0xd7), w(0xab), w(0x76), w(0xca), w(0x82), w(0xc9), \
|
||||
w(0x7d), w(0xfa), w(0x59), w(0x47), w(0xf0), w(0xad), w(0xd4), w(0xa2), w(0xaf), \
|
||||
w(0x9c), w(0xa4), w(0x72), w(0xc0), w(0xb7), w(0xfd), w(0x93), w(0x26), w(0x36), \
|
||||
w(0x3f), w(0xf7), w(0xcc), w(0x34), w(0xa5), w(0xe5), w(0xf1), w(0x71), w(0xd8), \
|
||||
w(0x31), w(0x15), w(0x04), w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), \
|
||||
w(0x9a), w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), w(0x75), \
|
||||
w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a), w(0xa0), w(0x52), \
|
||||
w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3), w(0x2f), w(0x84), w(0x53), w(0xd1), \
|
||||
w(0x00), w(0xed), w(0x20), w(0xfc), w(0xb1), w(0x5b), w(0x6a), w(0xcb), w(0xbe), \
|
||||
w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf), w(0xd0), w(0xef), w(0xaa), w(0xfb), \
|
||||
w(0x43), w(0x4d), w(0x33), w(0x85), w(0x45), w(0xf9), w(0x02), w(0x7f), w(0x50), \
|
||||
w(0x3c), w(0x9f), w(0xa8), w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), \
|
||||
w(0x38), w(0xf5), w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3), \
|
||||
w(0xd2), w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), w(0x97), w(0x44), w(0x17), \
|
||||
w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64), w(0x5d), w(0x19), w(0x73), w(0x60), \
|
||||
w(0x81), w(0x4f), w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88), w(0x46), w(0xee), \
|
||||
w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb), w(0xe0), w(0x32), w(0x3a), \
|
||||
w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c), w(0xc2), w(0xd3), w(0xac), w(0x62), \
|
||||
w(0x91), w(0x95), w(0xe4), w(0x79), w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), \
|
||||
w(0xd5), w(0x4e), w(0xa9), w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a), \
|
||||
w(0xae), w(0x08), w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c), w(0xa6), w(0xb4), \
|
||||
w(0xc6), w(0xe8), w(0xdd), w(0x74), w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a), \
|
||||
w(0x70), w(0x3e), w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e), w(0x61), \
|
||||
w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e), w(0xe1), w(0xf8), \
|
||||
w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), w(0x94), w(0x9b), w(0x1e), w(0x87), \
|
||||
w(0xe9), w(0xce), w(0x55), w(0x28), w(0xdf), w(0x8c), w(0xa1), w(0x89), w(0x0d), \
|
||||
w(0xbf), w(0xe6), w(0x42), w(0x68), w(0x41), w(0x99), w(0x2d), w(0x0f), w(0xb0), \
|
||||
w(0x54), w(0xbb), w(0x16) \
|
||||
}
|
||||
|
||||
#define isb_data(w) \
|
||||
{ \
|
||||
w(0x52), w(0x09), w(0x6a), w(0xd5), w(0x30), w(0x36), w(0xa5), w(0x38), w(0xbf), w(0x40), \
|
||||
w(0xa3), w(0x9e), w(0x81), w(0xf3), w(0xd7), w(0xfb), w(0x7c), w(0xe3), w(0x39), \
|
||||
w(0x82), w(0x9b), w(0x2f), w(0xff), w(0x87), w(0x34), w(0x8e), w(0x43), w(0x44), \
|
||||
w(0xc4), w(0xde), w(0xe9), w(0xcb), w(0x54), w(0x7b), w(0x94), w(0x32), w(0xa6), \
|
||||
w(0xc2), w(0x23), w(0x3d), w(0xee), w(0x4c), w(0x95), w(0x0b), w(0x42), w(0xfa), \
|
||||
w(0xc3), w(0x4e), w(0x08), w(0x2e), w(0xa1), w(0x66), w(0x28), w(0xd9), w(0x24), \
|
||||
w(0xb2), w(0x76), w(0x5b), w(0xa2), w(0x49), w(0x6d), w(0x8b), w(0xd1), w(0x25), \
|
||||
w(0x72), w(0xf8), w(0xf6), w(0x64), w(0x86), w(0x68), w(0x98), w(0x16), w(0xd4), \
|
||||
w(0xa4), w(0x5c), w(0xcc), w(0x5d), w(0x65), w(0xb6), w(0x92), w(0x6c), w(0x70), \
|
||||
w(0x48), w(0x50), w(0xfd), w(0xed), w(0xb9), w(0xda), w(0x5e), w(0x15), w(0x46), \
|
||||
w(0x57), w(0xa7), w(0x8d), w(0x9d), w(0x84), w(0x90), w(0xd8), w(0xab), w(0x00), \
|
||||
w(0x8c), w(0xbc), w(0xd3), w(0x0a), w(0xf7), w(0xe4), w(0x58), w(0x05), w(0xb8), \
|
||||
w(0xb3), w(0x45), w(0x06), w(0xd0), w(0x2c), w(0x1e), w(0x8f), w(0xca), w(0x3f), \
|
||||
w(0x0f), w(0x02), w(0xc1), w(0xaf), w(0xbd), w(0x03), w(0x01), w(0x13), w(0x8a), \
|
||||
w(0x6b), w(0x3a), w(0x91), w(0x11), w(0x41), w(0x4f), w(0x67), w(0xdc), w(0xea), \
|
||||
w(0x97), w(0xf2), w(0xcf), w(0xce), w(0xf0), w(0xb4), w(0xe6), w(0x73), w(0x96), \
|
||||
w(0xac), w(0x74), w(0x22), w(0xe7), w(0xad), w(0x35), w(0x85), w(0xe2), w(0xf9), \
|
||||
w(0x37), w(0xe8), w(0x1c), w(0x75), w(0xdf), w(0x6e), w(0x47), w(0xf1), w(0x1a), \
|
||||
w(0x71), w(0x1d), w(0x29), w(0xc5), w(0x89), w(0x6f), w(0xb7), w(0x62), w(0x0e), \
|
||||
w(0xaa), w(0x18), w(0xbe), w(0x1b), w(0xfc), w(0x56), w(0x3e), w(0x4b), w(0xc6), \
|
||||
w(0xd2), w(0x79), w(0x20), w(0x9a), w(0xdb), w(0xc0), w(0xfe), w(0x78), w(0xcd), \
|
||||
w(0x5a), w(0xf4), w(0x1f), w(0xdd), w(0xa8), w(0x33), w(0x88), w(0x07), w(0xc7), \
|
||||
w(0x31), w(0xb1), w(0x12), w(0x10), w(0x59), w(0x27), w(0x80), w(0xec), w(0x5f), \
|
||||
w(0x60), w(0x51), w(0x7f), w(0xa9), w(0x19), w(0xb5), w(0x4a), w(0x0d), w(0x2d), \
|
||||
w(0xe5), w(0x7a), w(0x9f), w(0x93), w(0xc9), w(0x9c), w(0xef), w(0xa0), w(0xe0), \
|
||||
w(0x3b), w(0x4d), w(0xae), w(0x2a), w(0xf5), w(0xb0), w(0xc8), w(0xeb), w(0xbb), \
|
||||
w(0x3c), w(0x83), w(0x53), w(0x99), w(0x61), w(0x17), w(0x2b), w(0x04), w(0x7e), \
|
||||
w(0xba), w(0x77), w(0xd6), w(0x26), w(0xe1), w(0x69), w(0x14), w(0x63), w(0x55), \
|
||||
w(0x21), w(0x0c), w(0x7d) \
|
||||
}
|
||||
|
||||
#define mm_data(w) \
|
||||
{ \
|
||||
w(0x00), w(0x01), w(0x02), w(0x03), w(0x04), w(0x05), w(0x06), w(0x07), w(0x08), w(0x09), \
|
||||
w(0x0a), w(0x0b), w(0x0c), w(0x0d), w(0x0e), w(0x0f), w(0x10), w(0x11), w(0x12), \
|
||||
w(0x13), w(0x14), w(0x15), w(0x16), w(0x17), w(0x18), w(0x19), w(0x1a), w(0x1b), \
|
||||
w(0x1c), w(0x1d), w(0x1e), w(0x1f), w(0x20), w(0x21), w(0x22), w(0x23), w(0x24), \
|
||||
w(0x25), w(0x26), w(0x27), w(0x28), w(0x29), w(0x2a), w(0x2b), w(0x2c), w(0x2d), \
|
||||
w(0x2e), w(0x2f), w(0x30), w(0x31), w(0x32), w(0x33), w(0x34), w(0x35), w(0x36), \
|
||||
w(0x37), w(0x38), w(0x39), w(0x3a), w(0x3b), w(0x3c), w(0x3d), w(0x3e), w(0x3f), \
|
||||
w(0x40), w(0x41), w(0x42), w(0x43), w(0x44), w(0x45), w(0x46), w(0x47), w(0x48), \
|
||||
w(0x49), w(0x4a), w(0x4b), w(0x4c), w(0x4d), w(0x4e), w(0x4f), w(0x50), w(0x51), \
|
||||
w(0x52), w(0x53), w(0x54), w(0x55), w(0x56), w(0x57), w(0x58), w(0x59), w(0x5a), \
|
||||
w(0x5b), w(0x5c), w(0x5d), w(0x5e), w(0x5f), w(0x60), w(0x61), w(0x62), w(0x63), \
|
||||
w(0x64), w(0x65), w(0x66), w(0x67), w(0x68), w(0x69), w(0x6a), w(0x6b), w(0x6c), \
|
||||
w(0x6d), w(0x6e), w(0x6f), w(0x70), w(0x71), w(0x72), w(0x73), w(0x74), w(0x75), \
|
||||
w(0x76), w(0x77), w(0x78), w(0x79), w(0x7a), w(0x7b), w(0x7c), w(0x7d), w(0x7e), \
|
||||
w(0x7f), w(0x80), w(0x81), w(0x82), w(0x83), w(0x84), w(0x85), w(0x86), w(0x87), \
|
||||
w(0x88), w(0x89), w(0x8a), w(0x8b), w(0x8c), w(0x8d), w(0x8e), w(0x8f), w(0x90), \
|
||||
w(0x91), w(0x92), w(0x93), w(0x94), w(0x95), w(0x96), w(0x97), w(0x98), w(0x99), \
|
||||
w(0x9a), w(0x9b), w(0x9c), w(0x9d), w(0x9e), w(0x9f), w(0xa0), w(0xa1), w(0xa2), \
|
||||
w(0xa3), w(0xa4), w(0xa5), w(0xa6), w(0xa7), w(0xa8), w(0xa9), w(0xaa), w(0xab), \
|
||||
w(0xac), w(0xad), w(0xae), w(0xaf), w(0xb0), w(0xb1), w(0xb2), w(0xb3), w(0xb4), \
|
||||
w(0xb5), w(0xb6), w(0xb7), w(0xb8), w(0xb9), w(0xba), w(0xbb), w(0xbc), w(0xbd), \
|
||||
w(0xbe), w(0xbf), w(0xc0), w(0xc1), w(0xc2), w(0xc3), w(0xc4), w(0xc5), w(0xc6), \
|
||||
w(0xc7), w(0xc8), w(0xc9), w(0xca), w(0xcb), w(0xcc), w(0xcd), w(0xce), w(0xcf), \
|
||||
w(0xd0), w(0xd1), w(0xd2), w(0xd3), w(0xd4), w(0xd5), w(0xd6), w(0xd7), w(0xd8), \
|
||||
w(0xd9), w(0xda), w(0xdb), w(0xdc), w(0xdd), w(0xde), w(0xdf), w(0xe0), w(0xe1), \
|
||||
w(0xe2), w(0xe3), w(0xe4), w(0xe5), w(0xe6), w(0xe7), w(0xe8), w(0xe9), w(0xea), \
|
||||
w(0xeb), w(0xec), w(0xed), w(0xee), w(0xef), w(0xf0), w(0xf1), w(0xf2), w(0xf3), \
|
||||
w(0xf4), w(0xf5), w(0xf6), w(0xf7), w(0xf8), w(0xf9), w(0xfa), w(0xfb), w(0xfc), \
|
||||
w(0xfd), w(0xfe), w(0xff) \
|
||||
}
|
||||
|
||||
#define rc_data(w) \
|
||||
{ w(0x01), w(0x02), w(0x04), w(0x08), w(0x10), w(0x20), w(0x40), w(0x80), w(0x1b), w(0x36) }
|
||||
|
||||
#define h0(x) (x)
|
||||
|
||||
#define w0(p) bytes2word(p, 0, 0, 0)
|
||||
#define w1(p) bytes2word(0, p, 0, 0)
|
||||
#define w2(p) bytes2word(0, 0, p, 0)
|
||||
#define w3(p) bytes2word(0, 0, 0, p)
|
||||
|
||||
#define u0(p) bytes2word(f2(p), p, p, f3(p))
|
||||
#define u1(p) bytes2word(f3(p), f2(p), p, p)
|
||||
#define u2(p) bytes2word(p, f3(p), f2(p), p)
|
||||
#define u3(p) bytes2word(p, p, f3(p), f2(p))
|
||||
|
||||
#define v0(p) bytes2word(fe(p), f9(p), fd(p), fb(p))
|
||||
#define v1(p) bytes2word(fb(p), fe(p), f9(p), fd(p))
|
||||
#define v2(p) bytes2word(fd(p), fb(p), fe(p), f9(p))
|
||||
#define v3(p) bytes2word(f9(p), fd(p), fb(p), fe(p))
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(STATIC_TABLES) || !defined(FF_TABLES)
|
||||
|
||||
#define f2(x) ((x << 1) ^ (((x >> 7) & 1) * WPOLY))
|
||||
#define f4(x) ((x << 2) ^ (((x >> 6) & 1) * WPOLY) ^ (((x >> 6) & 2) * WPOLY))
|
||||
#define f8(x) \
|
||||
((x << 3) ^ (((x >> 5) & 1) * WPOLY) ^ (((x >> 5) & 2) * WPOLY) ^ (((x >> 5) & 4) * WPOLY))
|
||||
#define f3(x) (f2(x) ^ x)
|
||||
#define f9(x) (f8(x) ^ x)
|
||||
#define fb(x) (f8(x) ^ f2(x) ^ x)
|
||||
#define fd(x) (f8(x) ^ f4(x) ^ x)
|
||||
#define fe(x) (f8(x) ^ f4(x) ^ f2(x))
|
||||
|
||||
#else
|
||||
|
||||
#define f2(x) ((x) ? pow[log[x] + 0x19] : 0)
|
||||
#define f3(x) ((x) ? pow[log[x] + 0x01] : 0)
|
||||
#define f9(x) ((x) ? pow[log[x] + 0xc7] : 0)
|
||||
#define fb(x) ((x) ? pow[log[x] + 0x68] : 0)
|
||||
#define fd(x) ((x) ? pow[log[x] + 0xee] : 0)
|
||||
#define fe(x) ((x) ? pow[log[x] + 0xdf] : 0)
|
||||
|
||||
#endif
|
||||
|
||||
#include "aestab.h"
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(STATIC_TABLES)
|
||||
|
||||
/* implemented in case of wrong call for fixed tables */
|
||||
|
||||
AES_RETURN aes_init(void) {
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#else /* Generate the tables for the dynamic table option */
|
||||
|
||||
#if defined(FF_TABLES)
|
||||
|
||||
#define gf_inv(x) ((x) ? pow[255 - log[x]] : 0)
|
||||
|
||||
#else
|
||||
|
||||
/* It will generally be sensible to use tables to compute finite
|
||||
field multiplies and inverses but where memory is scarse this
|
||||
code might sometimes be better. But it only has effect during
|
||||
initialisation so its pretty unimportant in overall terms.
|
||||
*/
|
||||
|
||||
/* return 2 ^ (n - 1) where n is the bit number of the highest bit
|
||||
set in x with x in the range 1 < x < 0x00000200. This form is
|
||||
used so that locals within fi can be bytes rather than words
|
||||
*/
|
||||
|
||||
static uint8_t hibit(const uint32_t x) {
|
||||
uint8_t r = (uint8_t)((x >> 1) | (x >> 2));
|
||||
|
||||
r |= (r >> 2);
|
||||
r |= (r >> 4);
|
||||
return (r + 1) >> 1;
|
||||
}
|
||||
|
||||
/* return the inverse of the finite field element x */
|
||||
|
||||
static uint8_t gf_inv(const uint8_t x) {
|
||||
uint8_t p1 = x, p2 = BPOLY, n1 = hibit(x), n2 = 0x80, v1 = 1, v2 = 0;
|
||||
|
||||
if(x < 2) return x;
|
||||
|
||||
for(;;) {
|
||||
if(n1)
|
||||
while(n2 >= n1) /* divide polynomial p2 by p1 */
|
||||
{
|
||||
n2 /= n1; /* shift smaller polynomial left */
|
||||
p2 ^= (p1 * n2) & 0xff; /* and remove from larger one */
|
||||
v2 ^= v1 * n2; /* shift accumulated value and */
|
||||
n2 = hibit(p2); /* add into result */
|
||||
}
|
||||
else
|
||||
return v1;
|
||||
|
||||
if(n2) /* repeat with values swapped */
|
||||
while(n1 >= n2) {
|
||||
n1 /= n2;
|
||||
p1 ^= p2 * n1;
|
||||
v1 ^= v2 * n1;
|
||||
n1 = hibit(p1);
|
||||
}
|
||||
else
|
||||
return v2;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* The forward and inverse affine transformations used in the S-box */
|
||||
uint8_t fwd_affine(const uint8_t x) {
|
||||
uint32_t w = x;
|
||||
w ^= (w << 1) ^ (w << 2) ^ (w << 3) ^ (w << 4);
|
||||
return 0x63 ^ ((w ^ (w >> 8)) & 0xff);
|
||||
}
|
||||
|
||||
uint8_t inv_affine(const uint8_t x) {
|
||||
uint32_t w = x;
|
||||
w = (w << 1) ^ (w << 3) ^ (w << 6);
|
||||
return 0x05 ^ ((w ^ (w >> 8)) & 0xff);
|
||||
}
|
||||
|
||||
static int init = 0;
|
||||
|
||||
AES_RETURN aes_init(void) {
|
||||
uint32_t i, w;
|
||||
|
||||
#if defined(FF_TABLES)
|
||||
|
||||
uint8_t pow[512] = {0}, log[256] = {0};
|
||||
|
||||
if(init) return EXIT_SUCCESS;
|
||||
/* log and power tables for GF(2^8) finite field with
|
||||
WPOLY as modular polynomial - the simplest primitive
|
||||
root is 0x03, used here to generate the tables
|
||||
*/
|
||||
|
||||
i = 0;
|
||||
w = 1;
|
||||
do {
|
||||
pow[i] = (uint8_t)w;
|
||||
pow[i + 255] = (uint8_t)w;
|
||||
log[w] = (uint8_t)i++;
|
||||
w ^= (w << 1) ^ (w & 0x80 ? WPOLY : 0);
|
||||
} while(w != 1);
|
||||
|
||||
#else
|
||||
if(init) return EXIT_SUCCESS;
|
||||
#endif
|
||||
|
||||
for(i = 0, w = 1; i < RC_LENGTH; ++i) {
|
||||
t_set(r, c)[i] = bytes2word(w, 0, 0, 0);
|
||||
w = f2(w);
|
||||
}
|
||||
|
||||
for(i = 0; i < 256; ++i) {
|
||||
uint8_t b;
|
||||
|
||||
b = fwd_affine(gf_inv((uint8_t)i));
|
||||
w = bytes2word(f2(b), b, b, f3(b));
|
||||
|
||||
#if defined(SBX_SET)
|
||||
t_set(s, box)[i] = b;
|
||||
#endif
|
||||
|
||||
#if defined(FT1_SET) /* tables for a normal encryption round */
|
||||
t_set(f, n)[i] = w;
|
||||
#endif
|
||||
#if defined(FT4_SET)
|
||||
t_set(f, n)[0][i] = w;
|
||||
t_set(f, n)[1][i] = upr(w, 1);
|
||||
t_set(f, n)[2][i] = upr(w, 2);
|
||||
t_set(f, n)[3][i] = upr(w, 3);
|
||||
#endif
|
||||
w = bytes2word(b, 0, 0, 0);
|
||||
|
||||
#if defined(FL1_SET) /* tables for last encryption round (may also */
|
||||
t_set(f, l)[i] = w; /* be used in the key schedule) */
|
||||
#endif
|
||||
#if defined(FL4_SET)
|
||||
t_set(f, l)[0][i] = w;
|
||||
t_set(f, l)[1][i] = upr(w, 1);
|
||||
t_set(f, l)[2][i] = upr(w, 2);
|
||||
t_set(f, l)[3][i] = upr(w, 3);
|
||||
#endif
|
||||
|
||||
#if defined(LS1_SET) /* table for key schedule if t_set(f,l) above is*/
|
||||
t_set(l, s)[i] = w; /* not of the required form */
|
||||
#endif
|
||||
#if defined(LS4_SET)
|
||||
t_set(l, s)[0][i] = w;
|
||||
t_set(l, s)[1][i] = upr(w, 1);
|
||||
t_set(l, s)[2][i] = upr(w, 2);
|
||||
t_set(l, s)[3][i] = upr(w, 3);
|
||||
#endif
|
||||
|
||||
b = gf_inv(inv_affine((uint8_t)i));
|
||||
w = bytes2word(fe(b), f9(b), fd(b), fb(b));
|
||||
|
||||
#if defined(IM1_SET) /* tables for the inverse mix column operation */
|
||||
t_set(i, m)[b] = w;
|
||||
#endif
|
||||
#if defined(IM4_SET)
|
||||
t_set(i, m)[0][b] = w;
|
||||
t_set(i, m)[1][b] = upr(w, 1);
|
||||
t_set(i, m)[2][b] = upr(w, 2);
|
||||
t_set(i, m)[3][b] = upr(w, 3);
|
||||
#endif
|
||||
|
||||
#if defined(ISB_SET)
|
||||
t_set(i, box)[i] = b;
|
||||
#endif
|
||||
#if defined(IT1_SET) /* tables for a normal decryption round */
|
||||
t_set(i, n)[i] = w;
|
||||
#endif
|
||||
#if defined(IT4_SET)
|
||||
t_set(i, n)[0][i] = w;
|
||||
t_set(i, n)[1][i] = upr(w, 1);
|
||||
t_set(i, n)[2][i] = upr(w, 2);
|
||||
t_set(i, n)[3][i] = upr(w, 3);
|
||||
#endif
|
||||
w = bytes2word(b, 0, 0, 0);
|
||||
#if defined(IL1_SET) /* tables for last decryption round */
|
||||
t_set(i, l)[i] = w;
|
||||
#endif
|
||||
#if defined(IL4_SET)
|
||||
t_set(i, l)[0][i] = w;
|
||||
t_set(i, l)[1][i] = upr(w, 1);
|
||||
t_set(i, l)[2][i] = upr(w, 2);
|
||||
t_set(i, l)[3][i] = upr(w, 3);
|
||||
#endif
|
||||
}
|
||||
init = 1;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
Automatic code initialisation (suggested by by Henrik S. Gaßmann)
|
||||
based on code provided by Joe Lowe and placed in the public domain at:
|
||||
http://stackoverflow.com/questions/1113409/attribute-constructor-equivalent-in-vc
|
||||
*/
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
#pragma section(".CRT$XCU", read)
|
||||
|
||||
__declspec(allocate(".CRT$XCU")) void(__cdecl* aes_startup)(void) = aes_init;
|
||||
|
||||
#elif defined(__GNUC__)
|
||||
|
||||
static void aes_startup(void) __attribute__((constructor));
|
||||
|
||||
static void aes_startup(void) {
|
||||
aes_init();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#pragma message("dynamic tables must be initialised manually on your system")
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
---------------------------------------------------------------------------
|
||||
Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved.
|
||||
|
||||
The redistribution and use of this software (with or without changes)
|
||||
is allowed without the payment of fees or royalties provided that:
|
||||
|
||||
source code distributions include the above copyright notice, this
|
||||
list of conditions and the following disclaimer;
|
||||
|
||||
binary distributions include the above copyright notice, this list
|
||||
of conditions and the following disclaimer in their documentation.
|
||||
|
||||
This software is provided 'as is' with no explicit or implied warranties
|
||||
in respect of its operation, including, but not limited to, correctness
|
||||
and fitness for purpose.
|
||||
---------------------------------------------------------------------------
|
||||
Issue Date: 20/12/2007
|
||||
|
||||
This file contains the code for declaring the tables needed to implement
|
||||
AES. The file aesopt.h is assumed to be included before this header file.
|
||||
If there are no global variables, the definitions here can be used to put
|
||||
the AES tables in a structure so that a pointer can then be added to the
|
||||
AES context to pass them to the AES routines that need them. If this
|
||||
facility is used, the calling program has to ensure that this pointer is
|
||||
managed appropriately. In particular, the value of the t_dec(in,it) item
|
||||
in the table structure must be set to zero in order to ensure that the
|
||||
tables are initialised. In practice the three code sequences in aeskey.c
|
||||
that control the calls to aes_init() and the aes_init() routine itself will
|
||||
have to be changed for a specific implementation. If global variables are
|
||||
available it will generally be preferable to use them with the precomputed
|
||||
STATIC_TABLES option that uses static global tables.
|
||||
|
||||
The following defines can be used to control the way the tables
|
||||
are defined, initialised and used in embedded environments that
|
||||
require special features for these purposes
|
||||
|
||||
the 't_dec' construction is used to declare fixed table arrays
|
||||
the 't_set' construction is used to set fixed table values
|
||||
the 't_use' construction is used to access fixed table values
|
||||
|
||||
256 byte tables:
|
||||
|
||||
t_xxx(s,box) => forward S box
|
||||
t_xxx(i,box) => inverse S box
|
||||
|
||||
256 32-bit word OR 4 x 256 32-bit word tables:
|
||||
|
||||
t_xxx(f,n) => forward normal round
|
||||
t_xxx(f,l) => forward last round
|
||||
t_xxx(i,n) => inverse normal round
|
||||
t_xxx(i,l) => inverse last round
|
||||
t_xxx(l,s) => key schedule table
|
||||
t_xxx(i,m) => key schedule table
|
||||
|
||||
Other variables and tables:
|
||||
|
||||
t_xxx(r,c) => the rcon table
|
||||
*/
|
||||
|
||||
#if !defined(_AESTAB_H)
|
||||
#define _AESTAB_H
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define t_dec(m, n) t_##m##n
|
||||
#define t_set(m, n) t_##m##n
|
||||
#define t_use(m, n) t_##m##n
|
||||
|
||||
#if defined(STATIC_TABLES)
|
||||
#if !defined(__GNUC__) && (defined(__MSDOS__) || defined(__WIN16__))
|
||||
/* make tables far data to avoid using too much DGROUP space (PG) */
|
||||
#define CONST const far
|
||||
#else
|
||||
#define CONST const
|
||||
#endif
|
||||
#else
|
||||
#define CONST
|
||||
#endif
|
||||
|
||||
#if defined(DO_TABLES)
|
||||
#define EXTERN
|
||||
#else
|
||||
#define EXTERN extern
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && defined(TABLE_ALIGN)
|
||||
#define ALIGN __declspec(align(TABLE_ALIGN))
|
||||
#else
|
||||
#define ALIGN
|
||||
#endif
|
||||
|
||||
#if defined(__WATCOMC__) && (__WATCOMC__ >= 1100)
|
||||
#define XP_DIR __cdecl
|
||||
#else
|
||||
#define XP_DIR
|
||||
#endif
|
||||
|
||||
#if defined(DO_TABLES) && defined(STATIC_TABLES)
|
||||
#define d_1(t, n, b, e) EXTERN ALIGN CONST XP_DIR t n[256] = b(e)
|
||||
#define d_4(t, n, b, e, f, g, h) EXTERN ALIGN CONST XP_DIR t n[4][256] = {b(e), b(f), b(g), b(h)}
|
||||
EXTERN ALIGN CONST uint32_t t_dec(r, c)[RC_LENGTH] = rc_data(w0);
|
||||
#else
|
||||
#define d_1(t, n, b, e) EXTERN ALIGN CONST XP_DIR t n[256]
|
||||
#define d_4(t, n, b, e, f, g, h) EXTERN ALIGN CONST XP_DIR t n[4][256]
|
||||
EXTERN ALIGN CONST uint32_t t_dec(r, c)[RC_LENGTH];
|
||||
#endif
|
||||
|
||||
#if defined(SBX_SET)
|
||||
d_1(uint8_t, t_dec(s, box), sb_data, h0);
|
||||
#endif
|
||||
#if defined(ISB_SET)
|
||||
d_1(uint8_t, t_dec(i, box), isb_data, h0);
|
||||
#endif
|
||||
|
||||
#if defined(FT1_SET)
|
||||
d_1(uint32_t, t_dec(f, n), sb_data, u0);
|
||||
#endif
|
||||
#if defined(FT4_SET)
|
||||
d_4(uint32_t, t_dec(f, n), sb_data, u0, u1, u2, u3);
|
||||
#endif
|
||||
|
||||
#if defined(FL1_SET)
|
||||
d_1(uint32_t, t_dec(f, l), sb_data, w0);
|
||||
#endif
|
||||
#if defined(FL4_SET)
|
||||
d_4(uint32_t, t_dec(f, l), sb_data, w0, w1, w2, w3);
|
||||
#endif
|
||||
|
||||
#if defined(IT1_SET)
|
||||
d_1(uint32_t, t_dec(i, n), isb_data, v0);
|
||||
#endif
|
||||
#if defined(IT4_SET)
|
||||
d_4(uint32_t, t_dec(i, n), isb_data, v0, v1, v2, v3);
|
||||
#endif
|
||||
|
||||
#if defined(IL1_SET)
|
||||
d_1(uint32_t, t_dec(i, l), isb_data, w0);
|
||||
#endif
|
||||
#if defined(IL4_SET)
|
||||
d_4(uint32_t, t_dec(i, l), isb_data, w0, w1, w2, w3);
|
||||
#endif
|
||||
|
||||
#if defined(LS1_SET)
|
||||
#if defined(FL1_SET)
|
||||
#undef LS1_SET
|
||||
#else
|
||||
d_1(uint32_t, t_dec(l, s), sb_data, w0);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(LS4_SET)
|
||||
#if defined(FL4_SET)
|
||||
#undef LS4_SET
|
||||
#else
|
||||
d_4(uint32_t, t_dec(l, s), sb_data, w0, w1, w2, w3);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(IM1_SET)
|
||||
d_1(uint32_t, t_dec(i, m), mm_data, v0);
|
||||
#endif
|
||||
#if defined(IM4_SET)
|
||||
d_4(uint32_t, t_dec(i, m), mm_data, v0, v1, v2, v3);
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
---------------------------------------------------------------------------
|
||||
Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved.
|
||||
|
||||
LICENSE TERMS
|
||||
|
||||
The redistribution and use of this software (with or without changes)
|
||||
is allowed without the payment of fees or royalties provided that:
|
||||
|
||||
1. source code distributions include the above copyright notice, this
|
||||
list of conditions and the following disclaimer;
|
||||
|
||||
2. binary distributions include the above copyright notice, this list
|
||||
of conditions and the following disclaimer in their documentation;
|
||||
|
||||
3. the name of the copyright holder is not used to endorse products
|
||||
built using this software without specific written permission.
|
||||
|
||||
DISCLAIMER
|
||||
|
||||
This software is provided 'as is' with no explicit or implied warranties
|
||||
in respect of its properties, including, but not limited to, correctness
|
||||
and/or fitness for purpose.
|
||||
---------------------------------------------------------------------------
|
||||
Issue Date: 20/12/2007
|
||||
*/
|
||||
|
||||
// Correct Output (for variable block size - AES_BLOCK_SIZE undefined):
|
||||
|
||||
// lengths: block = 16 bytes, key = 16 bytes
|
||||
// key = 2b7e151628aed2a6abf7158809cf4f3c
|
||||
// input = 3243f6a8885a308d313198a2e0370734
|
||||
// encrypt = 3925841d02dc09fbdc118597196a0b32
|
||||
// decrypt = 3243f6a8885a308d313198a2e0370734
|
||||
|
||||
// lengths: block = 16 bytes, key = 24 bytes
|
||||
// key = 2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da5
|
||||
// input = 3243f6a8885a308d313198a2e0370734
|
||||
// encrypt = f9fb29aefc384a250340d833b87ebc00
|
||||
// decrypt = 3243f6a8885a308d313198a2e0370734
|
||||
|
||||
// lengths: block = 16 bytes, key = 32 bytes
|
||||
// key = 2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d9045190cfe
|
||||
// input = 3243f6a8885a308d313198a2e0370734
|
||||
// encrypt = 1a6e6c2c662e7da6501ffb62bc9e93f3
|
||||
// decrypt = 3243f6a8885a308d313198a2e0370734
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "aes.h"
|
||||
#include "aestst.h"
|
||||
|
||||
void out_state(long s0, long s1, long s2, long s3) {
|
||||
printf("\n%08lx%08lx%08lx%08lx", s0, s1, s2, s3);
|
||||
}
|
||||
|
||||
void oblk(char m[], unsigned char v[], unsigned long n) {
|
||||
unsigned long i;
|
||||
|
||||
printf("\n%s", m);
|
||||
|
||||
for(i = 0; i < n; ++i) printf("%02x", v[i]);
|
||||
}
|
||||
|
||||
void message(const char* s) {
|
||||
printf("%s", s);
|
||||
}
|
||||
|
||||
unsigned char pih[32] = // hex digits of pi
|
||||
{0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98,
|
||||
0xa2, 0xe0, 0x37, 0x07, 0x34, 0x4a, 0x40, 0x93, 0x82, 0x22, 0x99,
|
||||
0xf3, 0x1d, 0x00, 0x82, 0xef, 0xa9, 0x8e, 0xc4, 0xe6, 0xc8};
|
||||
|
||||
unsigned char exh[32] = // hex digits of e
|
||||
{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15,
|
||||
0x88, 0x09, 0xcf, 0x4f, 0x3c, 0x76, 0x2e, 0x71, 0x60, 0xf3, 0x8b,
|
||||
0x4d, 0xa5, 0x6a, 0x78, 0x4d, 0x90, 0x45, 0x19, 0x0c, 0xfe};
|
||||
|
||||
unsigned char res[3][32] = {
|
||||
{0x39, 0x25, 0x84, 0x1d, 0x02, 0xdc, 0x09, 0xfb, 0xdc, 0x11, 0x85, 0x97, 0x19, 0x6a, 0x0b, 0x32},
|
||||
{0xf9, 0xfb, 0x29, 0xae, 0xfc, 0x38, 0x4a, 0x25, 0x03, 0x40, 0xd8, 0x33, 0xb8, 0x7e, 0xbc, 0x00},
|
||||
{0x1a, 0x6e, 0x6c, 0x2c, 0x66, 0x2e, 0x7d, 0xa6, 0x50, 0x1f, 0xfb, 0x62, 0xbc, 0x9e, 0x93, 0xf3}};
|
||||
|
||||
// void cycles(volatile uint64_t *rtn)
|
||||
// {
|
||||
// #if defined( _MSCVER )
|
||||
// __asm // read the Pentium Time Stamp Counter
|
||||
// { cpuid
|
||||
// rdtsc
|
||||
// mov ecx,rtn
|
||||
// mov [ecx],eax
|
||||
// mov [ecx+4],edx
|
||||
// cpuid
|
||||
// }
|
||||
// #elif defined( __GNUC__ )
|
||||
// #if defined(__aarch64__)
|
||||
// __asm__ __volatile__("mrs %0, cntvct_el0": "=r" (*rtn));
|
||||
// #else
|
||||
// __asm__ __volatile__("rdtsc": "=A" (*rtn));
|
||||
// #endif
|
||||
// #endif
|
||||
// }
|
||||
|
||||
int main(void) {
|
||||
unsigned char out[32], ret[32], err = 0;
|
||||
f_ectx alge[1];
|
||||
f_dctx algd[1];
|
||||
|
||||
aes_init();
|
||||
|
||||
message("\nRun tests for the AES algorithm");
|
||||
|
||||
memset(&alge, 0, sizeof(aes_encrypt_ctx));
|
||||
memset(&algd, 0, sizeof(aes_decrypt_ctx));
|
||||
|
||||
#if defined(AES_128)
|
||||
memset(out, 0xcc, 16);
|
||||
memset(ret, 0xcc, 16);
|
||||
printf("\n\n// lengths: block = 16, bytes, key = 16 bytes");
|
||||
f_enc_key128(alge, exh);
|
||||
oblk("// key = ", exh, 16);
|
||||
oblk("// input = ", pih, 16);
|
||||
do_enc(alge, pih, out, 1);
|
||||
oblk("// encrypt = ", out, 16);
|
||||
if(memcmp(out, res[0], 16)) {
|
||||
message(" error");
|
||||
err += 1;
|
||||
}
|
||||
f_dec_key128(algd, exh);
|
||||
do_dec(algd, out, ret, 1);
|
||||
oblk("// decrypt = ", ret, 16);
|
||||
if(memcmp(ret, pih, 16)) {
|
||||
message(" error");
|
||||
err += 2;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(AES_192)
|
||||
memset(out, 0xcc, 16);
|
||||
memset(ret, 0xcc, 16);
|
||||
printf("\n\n// lengths: block = 16, bytes, key = 24 bytes");
|
||||
f_enc_key192(alge, exh);
|
||||
oblk("// key = ", exh, 24);
|
||||
oblk("// input = ", pih, 16);
|
||||
do_enc(alge, pih, out, 1);
|
||||
oblk("// encrypt = ", out, 16);
|
||||
if(memcmp(out, res[1], 16)) {
|
||||
message(" error");
|
||||
err += 4;
|
||||
}
|
||||
f_dec_key192(algd, exh);
|
||||
do_dec(algd, out, ret, 1);
|
||||
oblk("// decrypt = ", ret, 16);
|
||||
if(memcmp(ret, pih, 16)) {
|
||||
message(" error");
|
||||
err += 8;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(AES_256)
|
||||
memset(out, 0xcc, 16);
|
||||
memset(ret, 0xcc, 16);
|
||||
printf("\n\n// lengths: block = 16, bytes, key = 32 bytes");
|
||||
f_enc_key256(alge, exh);
|
||||
oblk("// key = ", exh, 32);
|
||||
oblk("// input = ", pih, 16);
|
||||
do_enc(alge, pih, out, 1);
|
||||
oblk("// encrypt = ", out, 16);
|
||||
if(memcmp(out, res[2], 16)) {
|
||||
message(" error");
|
||||
err += 16;
|
||||
}
|
||||
f_dec_key256(algd, exh);
|
||||
do_dec(algd, out, ret, 1);
|
||||
oblk("// decrypt = ", ret, 16);
|
||||
if(memcmp(ret, pih, 16)) {
|
||||
message(" error");
|
||||
err += 32;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(!err)
|
||||
message("\n\nThese values are all correct\n\n");
|
||||
else
|
||||
message("\n\nSome values are in error\n\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
---------------------------------------------------------------------------
|
||||
Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved.
|
||||
|
||||
The redistribution and use of this software (with or without changes)
|
||||
is allowed without the payment of fees or royalties provided that:
|
||||
|
||||
source code distributions include the above copyright notice, this
|
||||
list of conditions and the following disclaimer;
|
||||
|
||||
binary distributions include the above copyright notice, this list
|
||||
of conditions and the following disclaimer in their documentation.
|
||||
|
||||
This software is provided 'as is' with no explicit or implied warranties
|
||||
in respect of its operation, including, but not limited to, correctness
|
||||
and fitness for purpose.
|
||||
---------------------------------------------------------------------------
|
||||
Issue Date: 20/12/2007
|
||||
*/
|
||||
|
||||
// The following definitions are required for testing only, They are not needed
|
||||
// for AES (Rijndael) implementation. They are used to allow C, C++ and DLL
|
||||
// data access and subroutine calls to be expressed in the same form in the
|
||||
// testing code.
|
||||
|
||||
#ifndef AESTST_H
|
||||
#define AESTST_H
|
||||
|
||||
#define f_info(x) (x)->inf.b[2]
|
||||
#define f_ectx aes_encrypt_ctx
|
||||
#define f_enc_key128(a, b) aes_encrypt_key128((b), (a))
|
||||
#define f_enc_key192(a, b) aes_encrypt_key192((b), (a))
|
||||
#define f_enc_key256(a, b) aes_encrypt_key256((b), (a))
|
||||
#define f_enc_key(a, b, c) aes_encrypt_key((b), (c), (a))
|
||||
#define f_enc_blk(a, b, c) aes_encrypt((b), (c), (a))
|
||||
|
||||
#define f_dctx aes_decrypt_ctx
|
||||
#define f_dec_key128(a, b) aes_decrypt_key128((b), (a))
|
||||
#define f_dec_key192(a, b) aes_decrypt_key192((b), (a))
|
||||
#define f_dec_key256(a, b) aes_decrypt_key256((b), (a))
|
||||
#define f_dec_key(a, b, c) aes_decrypt_key((b), (c), (a))
|
||||
#define f_dec_blk(a, b, c) aes_decrypt((b), (c), (a))
|
||||
|
||||
#define f_talign(a, b) aes_test_alignment_detection(b)
|
||||
#define f_mode_reset(a) aes_mode_reset(a)
|
||||
#define f_ecb_enc(a, b, c, d) aes_ecb_encrypt((b), (c), (d), (a))
|
||||
#define f_ecb_dec(a, b, c, d) aes_ecb_decrypt((b), (c), (d), (a))
|
||||
#define f_cbc_enc(a, b, c, d, e) aes_cbc_encrypt((b), (c), (d), (e), (a))
|
||||
#define f_cbc_dec(a, b, c, d, e) aes_cbc_decrypt((b), (c), (d), (e), (a))
|
||||
#define f_cfb_enc(a, b, c, d, e) aes_cfb_encrypt((b), (c), (d), (e), (a))
|
||||
#define f_cfb_dec(a, b, c, d, e) aes_cfb_decrypt((b), (c), (d), (e), (a))
|
||||
#define f_ofb_cry(a, b, c, d, e) aes_ofb_crypt((b), (c), (d), (e), (a))
|
||||
#define f_ctr_cry(a, b, c, d, e, f) aes_ctr_crypt((b), (c), (d), (e), (f), (a))
|
||||
|
||||
#define ek_name128 "aes_encrypt_key128"
|
||||
#define ek_name192 "aes_encrypt_key192"
|
||||
#define ek_name256 "aes_encrypt_key256"
|
||||
#define ek_name "aes_encrypt_key"
|
||||
#define eb_name "aes_encrypt"
|
||||
|
||||
#define dk_name128 "aes_decrypt_key128"
|
||||
#define dk_name192 "aes_decrypt_key192"
|
||||
#define dk_name256 "aes_decrypt_key256"
|
||||
#define dk_name "aes_decrypt_key"
|
||||
#define db_name "aes_decrypt"
|
||||
|
||||
#define eres_name "aes_mode_reset"
|
||||
#define ecbe_name "aes_ecb_encrypt"
|
||||
#define ecbd_name "aes_ecb_decrypt"
|
||||
#define cbce_name "aes_cbc_encrypt"
|
||||
#define cbcd_name "aes_cbc_decrypt"
|
||||
#define cfbe_name "aes_cfb_encrypt"
|
||||
#define cfbd_name "aes_cfb_decrypt"
|
||||
#define ofb_name "aes_ofb_crypt"
|
||||
#define ctr_name "aes_ctr_crypt"
|
||||
|
||||
#ifndef AES_N_BLOCK
|
||||
#define do_enc(a, b, c, d) f_enc_blk(a, b, c)
|
||||
#define do_dec(a, b, c, d) f_dec_blk(a, b, c)
|
||||
#else
|
||||
#define do_enc(a, b, c, d) f_ecb_enc(a, b, c, 1)
|
||||
#define do_dec(a, b, c, d) f_ecb_dec(a, b, c, 1)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,241 @@
|
||||
/**
|
||||
* Copyright (c) 2017 Saleem Rashid
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, E1PRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "base32.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
const char* BASE32_ALPHABET_RFC4648 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ23456789";
|
||||
|
||||
static inline void base32_5to8(const uint8_t* in, uint8_t length, uint8_t* out);
|
||||
static inline bool
|
||||
base32_8to5(const uint8_t* in, uint8_t length, uint8_t* out, const char* alphabet);
|
||||
static inline void base32_8to5_raw(const uint8_t* in, uint8_t length, uint8_t* out);
|
||||
|
||||
static inline int base32_encode_character(uint8_t decoded, const char* alphabet);
|
||||
static inline int base32_decode_character(char encoded, const char* alphabet);
|
||||
|
||||
char* base32_encode(
|
||||
const uint8_t* in,
|
||||
size_t inlen,
|
||||
char* out,
|
||||
size_t outlen,
|
||||
const char* alphabet) {
|
||||
size_t length = base32_encoded_length(inlen);
|
||||
if(outlen <= length) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
base32_encode_unsafe(in, inlen, (uint8_t*)out);
|
||||
|
||||
for(size_t i = 0; i < length; i++) {
|
||||
int ret = base32_encode_character(out[i], alphabet);
|
||||
|
||||
if(ret == -1) {
|
||||
return NULL;
|
||||
} else {
|
||||
out[i] = ret;
|
||||
}
|
||||
}
|
||||
|
||||
out[length] = '\0';
|
||||
return &out[length];
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
base32_decode(const char* in, size_t inlen, uint8_t* out, size_t outlen, const char* alphabet) {
|
||||
size_t length = base32_decoded_length(inlen);
|
||||
if(outlen < length) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(!base32_decode_unsafe((uint8_t*)in, inlen, (uint8_t*)out, alphabet)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &out[length];
|
||||
}
|
||||
|
||||
void base32_encode_unsafe(const uint8_t* in, size_t inlen, uint8_t* out) {
|
||||
uint8_t remainder = inlen % 5;
|
||||
size_t limit = inlen - remainder;
|
||||
|
||||
size_t i = 0, j = 0;
|
||||
for(i = 0, j = 0; i < limit; i += 5, j += 8) {
|
||||
base32_5to8(&in[i], 5, &out[j]);
|
||||
}
|
||||
|
||||
if(remainder) base32_5to8(&in[i], remainder, &out[j]);
|
||||
}
|
||||
|
||||
bool base32_decode_unsafe(const uint8_t* in, size_t inlen, uint8_t* out, const char* alphabet) {
|
||||
uint8_t remainder = inlen % 8;
|
||||
size_t limit = inlen - remainder;
|
||||
|
||||
size_t i = 0, j = 0;
|
||||
for(i = 0, j = 0; i < limit; i += 8, j += 5) {
|
||||
if(!base32_8to5(&in[i], 8, &out[j], alphabet)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(remainder && !base32_8to5(&in[i], remainder, &out[j], alphabet)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t base32_encoded_length(size_t inlen) {
|
||||
uint8_t remainder = inlen % 5;
|
||||
|
||||
return (inlen / 5) * 8 + (remainder * 8 + 4) / 5;
|
||||
}
|
||||
|
||||
size_t base32_decoded_length(size_t inlen) {
|
||||
uint8_t remainder = inlen % 8;
|
||||
|
||||
return (inlen / 8) * 5 + (remainder * 5) / 8;
|
||||
}
|
||||
|
||||
void base32_5to8(const uint8_t* in, uint8_t length, uint8_t* out) {
|
||||
if(length >= 1) {
|
||||
out[0] = (in[0] >> 3);
|
||||
out[1] = (in[0] & 7) << 2;
|
||||
}
|
||||
|
||||
if(length >= 2) {
|
||||
out[1] |= (in[1] >> 6);
|
||||
out[2] = (in[1] >> 1) & 31;
|
||||
out[3] = (in[1] & 1) << 4;
|
||||
}
|
||||
|
||||
if(length >= 3) {
|
||||
out[3] |= (in[2] >> 4);
|
||||
out[4] = (in[2] & 15) << 1;
|
||||
}
|
||||
|
||||
if(length >= 4) {
|
||||
out[4] |= (in[3] >> 7);
|
||||
out[5] = (in[3] >> 2) & 31;
|
||||
out[6] = (in[3] & 3) << 3;
|
||||
}
|
||||
|
||||
if(length >= 5) {
|
||||
out[6] |= (in[4] >> 5);
|
||||
out[7] = (in[4] & 31);
|
||||
}
|
||||
}
|
||||
|
||||
bool base32_8to5(const uint8_t* in, uint8_t length, uint8_t* out, const char* alphabet) {
|
||||
if(length == 1 || length == 3 || length == 6 || length > 8) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(alphabet) {
|
||||
uint8_t decoded[length];
|
||||
memset(decoded, 0, sizeof(decoded));
|
||||
|
||||
for(size_t i = 0; i < length; i++) {
|
||||
int ret = base32_decode_character(in[i], alphabet);
|
||||
|
||||
if(ret == -1) {
|
||||
return false;
|
||||
} else {
|
||||
decoded[i] = ret;
|
||||
}
|
||||
}
|
||||
|
||||
base32_8to5_raw(decoded, length, out);
|
||||
} else {
|
||||
base32_8to5_raw(in, length, out);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void base32_8to5_raw(const uint8_t* in, uint8_t length, uint8_t* out) {
|
||||
if(length >= 2) {
|
||||
out[0] = (in[0] << 3);
|
||||
out[0] |= (in[1] >> 2);
|
||||
}
|
||||
|
||||
if(length >= 4) {
|
||||
out[1] = (in[1] & 3) << 6;
|
||||
out[1] |= (in[2] << 1);
|
||||
out[1] |= (in[3] >> 4);
|
||||
}
|
||||
|
||||
if(length >= 5) {
|
||||
out[2] = (in[3] & 15) << 4;
|
||||
out[2] |= (in[4] >> 1);
|
||||
}
|
||||
|
||||
if(length >= 7) {
|
||||
out[3] = (in[4] & 1) << 7;
|
||||
out[3] |= (in[5] << 2);
|
||||
out[3] |= (in[6] >> 3);
|
||||
}
|
||||
|
||||
if(length >= 8) {
|
||||
out[4] = (in[6] & 7) << 5;
|
||||
out[4] |= (in[7] & 31);
|
||||
}
|
||||
}
|
||||
|
||||
int base32_encode_character(uint8_t decoded, const char* alphabet) {
|
||||
if(decoded >> 5) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(alphabet == BASE32_ALPHABET_RFC4648) {
|
||||
if(decoded < 26) {
|
||||
return 'A' + decoded;
|
||||
} else {
|
||||
return '2' - 26 + decoded;
|
||||
}
|
||||
}
|
||||
|
||||
return alphabet[decoded];
|
||||
}
|
||||
|
||||
int base32_decode_character(char encoded, const char* alphabet) {
|
||||
if(alphabet == BASE32_ALPHABET_RFC4648) {
|
||||
if(encoded >= 'A' && encoded <= 'Z') {
|
||||
return encoded - 'A';
|
||||
} else if(encoded >= 'a' && encoded <= 'z') {
|
||||
return encoded - 'a';
|
||||
} else if(encoded >= '2' && encoded <= '7') {
|
||||
return encoded - '2' + 26;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
const char* occurrence = strchr(alphabet, encoded);
|
||||
|
||||
if(occurrence) {
|
||||
return occurrence - alphabet;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) 2017 Saleem Rashid
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __BASE32_H__
|
||||
#define __BASE32_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
extern const char* BASE32_ALPHABET_RFC4648;
|
||||
|
||||
char* base32_encode(const uint8_t* in, size_t inlen, char* out, size_t outlen, const char* alphabet);
|
||||
void base32_encode_unsafe(const uint8_t* in, size_t inlen, uint8_t* out);
|
||||
|
||||
uint8_t*
|
||||
base32_decode(const char* in, size_t inlen, uint8_t* out, size_t outlen, const char* alphabet);
|
||||
bool base32_decode_unsafe(const uint8_t* in, size_t inlen, uint8_t* out, const char* alphabet);
|
||||
|
||||
size_t base32_encoded_length(size_t inlen);
|
||||
size_t base32_decoded_length(size_t inlen);
|
||||
|
||||
#endif
|
||||