Skip to content

Firmware Update Architecture

The Super73 ebike display consists of two MCUs on the display board, plus an external motor controller connected via CAN bus.

Address locations on this page are for NRF version 221122.

┌─────────────────────────────────────────────────────────────────────────┐
│ DISPLAY DEVICE │
│ │
│ ┌─────────────────────────┐ ┌─────────────────────────────────┐ │
│ │ nRF52832 │ UART │ STM32F0xx │ │
│ │ (BLE + Main CPU) │◄────►│ (UART↔CAN Bridge) │ │
│ │ │ │ │ │
│ │ • BLE DFU Service │ │ • Receives $NRF,CAN,... cmds │ │
│ │ • ECDSA P-256 verify │ │ • Forwards to CAN bus │ │
│ │ • External SPI Flash │ │ • Routes CAN responses back │ │
│ │ • Application logic │ │ │ │
│ └──────────┬──────────────┘ └──────────────┬──────────────────┘ │
│ │ SPI │ │
│ ┌──────────▼──────────────┐ │ │
│ │ W25Q External Flash │ │ │
│ │ (Firmware storage) │ │ │
│ └──────────────────────────┘ │ │
└───────────────────────────────────────────────────┼───────────────────────┘
│ CAN BUS
┌──────────────────────────────┐
│ MOTOR CONTROLLER │
│ (STM32/GD32 based) │
│ │
│ • Simple CAN bootloader │
│ • NO signature verification │
│ • 8-bit checksum only │
└──────────────────────────────┘

The system supports multiple firmware update targets:

TypeValueTargetMax SizeStored In
0nRF AppnRF52832 application~200KBInternal flash
1SoftDeviceNordic BLE stackN/AInternal flash
2MCU/ControllerMotor controller45KB (0xB000)External flash → CAN
3BothnRF + Motor controllerCombinedBoth

Updates are delivered as signed nrfutil DFU packages containing:

┌────────────────────────────────────────────────────────────────┐
│ DFU Package (.zip) │
├────────────────────────────────────────────────────────────────┤
│ manifest.json │
│ ├── init packet (dat file) │
│ └── firmware binary (bin file) │
└────────────────────────────────────────────────────────────────┘
OffsetSizeFieldDescription
0x044app_typeApplication type flags
0x084hash_type0x20=SHA256-32, 0x40=SHA256-64, 0x80=SHA256-128
0x0C1has_signatureSignature present flag
0x104signature_typeExpected: 0x34 (ECDSA P-256)
0x141fw_version_countNumber of compatible versions
0x184×Nfw_versions[]Array of compatible SoftDevice IDs
0x551update_type0=nRF, 1=SD, 2=MCU, 3=Both
0x591target_typeTarget device type
0x5A1has_mcu_sizeMCU size field present
0x5C4mcu_fw_sizeMCU firmware size
0x601has_ext_sizeExtended size present
0x644ext_fw_sizeExtended firmware size
0x681has_nrf_sizenRF size present
0x6C4nrf_fw_sizenRF firmware size
0x7332fw_hashSHA-256 hash of firmware
0x931init_validInit packet valid flag
0x941signature_validSignature validation flag
0xA11signature_lenECDSA signature length
0xA264signatureECDSA P-256 signature (r,s)
┌──────────────┐ ┌─────────────────────────────────────────────┐
│ Phone │ BLE │ nRF52 Bootloader │
│ nrfutil │────────►│ │
└──────────────┘ │ 1. Receive init packet │
│ 2. Validate structure │
│ 3. Check SoftDevice compatibility │
│ 4. Verify ECDSA P-256 signature │
│ 5. Receive firmware data chunks │
│ 6. Write to flash (internal or external) │
│ 7. Verify hash │
│ 8. Mark as valid │
└─────────────────────────────────────────────┘

The bootloader reads the device’s SoftDevice ID from address 0x300C:

Device Info (0x3000):
┌────────┬─────────────┬────────────────────────────────┐
│ Offset │ Value │ Meaning │
├────────┼─────────────┼────────────────────────────────┤
│ 0x3008 │ 0x00023000 │ Application base address │
│ 0x300C │ 0x00A5 │ SoftDevice ID (S132 v7.0.1) │
│ 0x3010 │ 0x84 │ SoftDevice size (132 × 4KB) │
│ 0x3014 │ 5,001,000 │ FW version 5.1.0 │
└────────┴─────────────┴────────────────────────────────┘
Version format: MAJOR × 1,000,000 + MINOR × 1,000 + PATCH
Example: 5,001,000 = version 5.1.0

The DFU package must include 0x00A5 in its fw_versions[] array to be accepted.

┌─────────────────────────────────────────────────────────────────────┐
│ ECDSA P-256 Verification │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 1. Hash init packet fields with SHA-256 │
│ │
│ 2. Extract signature (r, s) from init packet │
│ │
│ 3. Verify using public key embedded in bootloader: │
│ - Curve: NIST P-256 (secp256r1) │
│ - Key location: Bootloader flash (0x7B5F4 area) │
│ │
│ 4. If verification fails → reject DFU │
│ │
└─────────────────────────────────────────────────────────────────────┘
Key Functions:
signature_verify_init() @ 0x77648
ecdsa_verify() @ 0x775b4
ec_scalar_mult() @ 0x738d8

For motor controller updates (type 2), firmware is stored in external SPI flash:

External Flash Layout:
┌─────────────────────────────────────────────────────────────────────┐
│ W25Q SPI Flash │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Bank 0 (g_flash_bank0_addr): Primary storage │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Header (5 bytes) │ │
│ │ ├─ Bytes 0-3: CRC32 (big-endian) │ │
│ │ └─ Byte 4: Status (0xF0=valid, 0xFE=in progress) │ │
│ ├─────────────────────────────────────────────────────────────┤ │
│ │ Firmware data (raw bytes, NO signature) │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ Bank 1 (g_flash_bank1_addr): Backup / MCU firmware │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Header (5 bytes) + Firmware data │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ Bank 2 (g_flash_bank2_addr): Update staging │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Header (8 bytes) + Firmware data │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
DFU Reception:
┌─────────────────────┐
Init Packet ─────► Verify signature ───►│ DISCARDED │
(signature, in RAM │ (not stored) │
versions, └─────────────────────┘
hash, etc.)
┌─────────────────────┐
Firmware ─────► Write to external ──►│ STORED IN FLASH │
Binary flash │ (raw bytes only) │
└─────────────────────┘
External flash contains:
✓ Firmware size (in header)
✓ Status byte (in header)
✓ Raw firmware bytes
✗ NO signature
✗ NO init packet
✗ NO version info
✗ NO hash for re-verification

After reboot, the nRF52 application detects pending firmware and sends it to the motor controller:

┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ External │ │ nRF52 │ │ STM32 │ │ Motor │
│ Flash │ │ App │ │ Bridge │ │ Controller │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │ │
│ Read header │ │ │
│◄─────────────────│ │ │
│ (size, status) │ │ │
│ │ │ │
│ │ $NRF,CAN,FILTER │ │
│ │─────────────────►│ │
│ │ │ │
│ │ Enter bootloader │ │
│ │─────────────────►│─────────────────►│
│ │ │ CAN 0x67F │
│ │ │ │
│ │ │◄─────────────────│
│ │◄─────────────────│ ACK 0x5AA │
│ │ │ │
│ │ Erase command │ │
│ │─────────────────►│─────────────────►│
│ │ │ │
│ Read firmware │ │ │
│◄─────────────────│ │ │
│ (raw bytes) │ │ │
│ │ │ │
│ │ Data chunks │ │
│ │─────────────────►│─────────────────►│
│ │ + 8-bit checksum │ │
│ │ │ │
│ │ Jump command │ │
│ │─────────────────►│─────────────────►│
│ │ │ │
Standard CAN (11-bit ID):
$NRF,5,<flags>,<id_hi>,<id_lo>,<d0>,<d1>,<d2>,<d3>,<d4>,<d5>,<d6>,<d7>*
Extended CAN (29-bit ID):
$NRF,6,<flags>,<id_b3>,<id_b2>,<id_b1>,<id_b0>,<d0>...<d7>,<checksum>*
Examples:
$NRF,CAN,READY - CAN bus ready check
$NRF,CAN,BAUD,KB,1f4* - Set baud rate
$NRF,CAN,FILTER,...* - Configure message filters

CAN Protocol (STM32 Bridge → Motor Controller)

Section titled “CAN Protocol (STM32 Bridge → Motor Controller)”

Type 1 - Standard CAN (11-bit IDs):

DirectionCAN IDPurpose
TX0x67FCommands to motor controller
RX0x5FFCommand responses
RX0x5AAExtended responses

Type 2 - Extended CAN (29-bit IDs):

DirectionCAN IDPurpose
TX0x14830111Erase command
TX0x14840111Init/size command
TX0x14860111Data chunks
TX0x14870111End/jump command
┌─────────────────────────────────────────────────────────────────────┐
│ Motor Controller Update States │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ State 1: ENTER BOOTLOADER │
│ │ - Configure CAN filters for update mode │
│ │ - Send bootloader entry command │
│ │ - Wait for ACK (0x5AA: AA 55 2A 00 2A 00) │
│ ▼ │
│ State 2: ERASE │
│ │ - Send erase command │
│ │ - Wait for erase ACK (0x5FF: 60 1F 51 ...) │
│ ▼ │
│ State 3: INITIALIZE │
│ │ - Send firmware size and metadata │
│ │ - Wait for init ACK (0x5FF: 60 1F 50 ...) │
│ ▼ │
│ State 4: TRANSFER DATA │
│ │ - Read chunks from external flash │
│ │ - Send via CAN with 8-bit checksum │
│ │ - Wait for ACK after each chunk │
│ │ - Retry up to 3× on failure │
│ ▼ │
│ State 5: VERIFY │
│ │ - Confirm all data sent │
│ │ - Check completion status │
│ ▼ │
│ State 6: FINALIZE │
│ - Send jump/end command ("UEND") │
│ - Motor controller boots new firmware │
│ │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ CRYPTOGRAPHICALLY VERIFIED │ │
│ │ │ │
│ │ Phone ──BLE──► nRF52 Bootloader │ │
│ │ • ECDSA P-256 signature ✓ │ │
│ │ • SHA-256 hash ✓ │ │
│ │ • Version compatibility ✓ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ NO CRYPTOGRAPHIC VERIFICATION │ │
│ │ │ │
│ │ External Flash ──► nRF52 App ──► STM32 ──► Motor Ctrl │ │
│ │ │ │
│ │ • Raw firmware bytes only │ │
│ │ • 8-bit additive checksum (transmission errors only) │ │
│ │ • No signature verification │ │
│ │ • No hash verification │ │
│ │ • No anti-rollback protection │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
PropertyBLE→nRF52Flash→Motor Ctrl
Signature verification✓ ECDSA P-256✗ None
Hash verification✓ SHA-256✗ None
Version checking✓ SoftDevice ID✗ None
Anti-rollback✗ None✗ None
Integrity on storage✗ Not re-verified✗ N/A
Transport integrity✓ BLE CRC8-bit checksum
  1. External Flash Modification

    • If attacker has SPI access to external flash
    • Can modify stored firmware after signature verification
    • Application sends modified firmware to motor controller
    • No detection mechanism
  2. CAN Bus Injection

    • If attacker has CAN bus access
    • Can send arbitrary firmware to motor controller
    • Motor controller has no signature verification
    • Only needs correct CAN IDs and 8-bit checksums
  3. Firmware Rollback

    • No anti-rollback protection on any component
    • Can flash older vulnerable firmware versions
    • nRF52 checks SoftDevice compatibility only
// 8-bit additive checksum - for transmission errors only
uint8_t calculate_checksum(uint8_t* data, int len) {
uint16_t sum = 0;
for (int i = 0; i < len; i++)
sum += data[i];
return (uint8_t)sum; // truncate to 8 bits
}
AddressFunctionPurpose
0x7677cdfu_init_packet_validateValidate init packet and signature
0x77adcdfu_cmd_init_handlerProcess DFU init commands
0x77cd0dfu_cmd_data_handlerProcess DFU data commands
0x781b0dfu_complete_handlerFinalize DFU transfer
0x775b4ecdsa_verifyECDSA P-256 signature verification
0x77648signature_verify_initInitialize signature verification
0x7a398dfu_ext_flash_writeWrite firmware to external flash
0x7b03cflash_bank_write_headerWrite bank header (size + status)
AddressFunctionPurpose
0x32068vehicle_state_update_handlerCheck for pending updates
0x3182cmotor_controller_fw_update_state_machineMain update state machine
0x30818vehicle_update_handlerHandle update completion
0x30b7cprocess_can_messageProcess CAN responses
0x3889cext_flash_get_stored_infoGet stored firmware info
0x38840ext_flash_read_stored_dataRead firmware from flash
0x2f66csend_nrf_protocol_messageSend CAN message via UART
0x31490calculate_checksum8-bit additive checksum
┌─────────────────┐
│ nrfutil DFU │
│ Package │
│ (signed .zip) │
└────────┬────────┘
│ BLE
┌─────────────────┐
│ nRF52 Boot- │──► ECDSA P-256 Signature Verify
│ loader │──► SHA-256 Hash Verify
│ │──► SoftDevice Version Check
└────────┬────────┘
┌─────────────────┐
│ External SPI │
│ Flash Bank │──► Header: size + status (5-8 bytes)
│ (W25Q series) │──► Data: raw firmware (NO signature)
└────────┬────────┘
┌─────────────────┐
│ nRF52 App │──► Read from ext flash (no verification)
│ │──► State machine for CAN transfer
└────────┬────────┘
│ UART ($NRF,5/6,...)
┌─────────────────┐
│ STM32 Bridge │──► Parse ASCII protocol
│ (on display) │──► Convert to CAN frames
└────────┬────────┘
│ CAN Bus (0x67F, 0x5FF, 0x5AA, 0x148x0111)
┌─────────────────┐
│ Motor │──► Bootloader receives (no sig check)
│ Controller │──► 8-bit checksum only
│ (STM32/GD32) │──► Erase → Write → Jump
└─────────────────┘