finish bootloader
This commit is contained in:
parent
b05f3cc9e8
commit
4cb4be21d9
4
Makefile
4
Makefile
@ -39,6 +39,10 @@ efm32prog:
|
|||||||
cd '.\efm32\GNU ARM v7.2.1 - Debug' && $(MAKE) all
|
cd '.\efm32\GNU ARM v7.2.1 - Debug' && $(MAKE) all
|
||||||
commander flash '.\efm32\GNU ARM v7.2.1 - Debug\EFM32.hex' -s 440121060
|
commander flash '.\efm32\GNU ARM v7.2.1 - Debug\EFM32.hex' -s 440121060
|
||||||
|
|
||||||
|
efm32bootprog:
|
||||||
|
cd '.\efm32boot\GNU ARM v7.2.1 - Debug' && $(MAKE) all
|
||||||
|
commander flash '.\efm32boot\GNU ARM v7.2.1 - Debug\efm32boot.hex' -s 440121060
|
||||||
|
|
||||||
$(name): $(obj)
|
$(name): $(obj)
|
||||||
$(CC) $(LDFLAGS) -o $@ $(obj) $(LDFLAGS)
|
$(CC) $(LDFLAGS) -o $@ $(obj) $(LDFLAGS)
|
||||||
|
|
||||||
|
@ -29,6 +29,14 @@
|
|||||||
<folderInfo id="com.silabs.ss.tool.ide.arm.toolchain.gnu.cdt.debug#com.silabs.ss.tool.ide.arm.toolchain.gnu.cdt:7.2.1.20170904." name="/" resourcePath="">
|
<folderInfo id="com.silabs.ss.tool.ide.arm.toolchain.gnu.cdt.debug#com.silabs.ss.tool.ide.arm.toolchain.gnu.cdt:7.2.1.20170904." name="/" resourcePath="">
|
||||||
<toolChain id="com.silabs.ide.si32.gcc.cdt.managedbuild.toolchain.exe.1911691633" name="Si32 GNU ARM" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.toolchain.exe">
|
<toolChain id="com.silabs.ide.si32.gcc.cdt.managedbuild.toolchain.exe.1911691633" name="Si32 GNU ARM" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.toolchain.exe">
|
||||||
<option id="com.silabs.ide.si32.gcc.cdt.managedbuild.toolchain.debug.level.50213284" name="Debug Level" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.toolchain.debug.level" value="com.silabs.ide.si32.gcc.cdt.managedbuild.toolchain.debug.level.default" valueType="enumerated"/>
|
<option id="com.silabs.ide.si32.gcc.cdt.managedbuild.toolchain.debug.level.50213284" name="Debug Level" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.toolchain.debug.level" value="com.silabs.ide.si32.gcc.cdt.managedbuild.toolchain.debug.level.default" valueType="enumerated"/>
|
||||||
|
<option id="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.linker.usescript.567506607" name="Use custom linker script" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.linker.usescript" value="false" valueType="boolean"/>
|
||||||
|
<option id="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.linker.script.583769327" name="Linker Script:" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.linker.script" value="C:\Users\conor\Desktop\u2f-one\efm32\boot\EFM32.ld" valueType="string"/>
|
||||||
|
<option id="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.linker.overrideflash.89245522" name="Override default flash options" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.linker.overrideflash" value="true" valueType="boolean"/>
|
||||||
|
<option id="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.linker.overrideram.83367783" name="Override default RAM options" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.linker.overrideram" value="true" valueType="boolean"/>
|
||||||
|
<option id="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.linker.flashorigin.1326657156" name="ORIGIN" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.linker.flashorigin" value="0x8000" valueType="string"/>
|
||||||
|
<option id="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.linker.flashlength.1545864710" name="LENGTH" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.linker.flashlength" value="0x38000" valueType="string"/>
|
||||||
|
<option id="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.linker.ramorigin.428840301" name="ORIGIN" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.linker.ramorigin" value="0x20000000" valueType="string"/>
|
||||||
|
<option id="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.linker.ramlength.1809448749" name="LENGTH" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.linker.ramlength" value="0x8000" valueType="string"/>
|
||||||
<targetPlatform binaryParser="org.eclipse.cdt.core.ELF;org.eclipse.cdt.core.GNU_ELF;com.silabs.ss.framework.debugger.core.BIN;com.silabs.ss.framework.debugger.core.HEX;com.silabs.ss.framework.debugger.core.S37;com.silabs.ss.framework.debugger.core.EBL;com.silabs.ss.framework.debugger.core.GBL" id="com.silabs.ide.si32.gcc.cdt.managedbuild.target.gnu.platform.base.1696251138" isAbstract="false" name="Debug Platform" osList="win32,linux,macosx" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.target.gnu.platform.base"/>
|
<targetPlatform binaryParser="org.eclipse.cdt.core.ELF;org.eclipse.cdt.core.GNU_ELF;com.silabs.ss.framework.debugger.core.BIN;com.silabs.ss.framework.debugger.core.HEX;com.silabs.ss.framework.debugger.core.S37;com.silabs.ss.framework.debugger.core.EBL;com.silabs.ss.framework.debugger.core.GBL" id="com.silabs.ide.si32.gcc.cdt.managedbuild.target.gnu.platform.base.1696251138" isAbstract="false" name="Debug Platform" osList="win32,linux,macosx" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.target.gnu.platform.base"/>
|
||||||
<builder buildPath="${workspace_loc:/EFM32}/GNU ARM v7.2.1 - Debug" id="com.silabs.ide.si32.gcc.cdt.managedbuild.target.gnu.builder.base.819523820" keepEnvironmentInBuildfile="false" managedBuildOn="true" name="Si32 GNU ARM Builder" parallelBuildOn="true" parallelizationNumber="optimal" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.target.gnu.builder.base">
|
<builder buildPath="${workspace_loc:/EFM32}/GNU ARM v7.2.1 - Debug" id="com.silabs.ide.si32.gcc.cdt.managedbuild.target.gnu.builder.base.819523820" keepEnvironmentInBuildfile="false" managedBuildOn="true" name="Si32 GNU ARM Builder" parallelBuildOn="true" parallelizationNumber="optimal" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.target.gnu.builder.base">
|
||||||
<outputEntries>
|
<outputEntries>
|
||||||
@ -218,7 +226,7 @@
|
|||||||
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
|
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
|
||||||
</cconfiguration>
|
</cconfiguration>
|
||||||
</storageModule>
|
</storageModule>
|
||||||
<storageModule moduleId="com.silabs.ss.framework.ide.project.core.cpp" project.generation="74" projectCommon.boardIds="brd2500a:0.0.0" projectCommon.buildArtifactType="EXE" projectCommon.importModeId="COPY" projectCommon.partId="mcu.arm.efm32.pg1.efm32pg1b200f256gm48" projectCommon.sdkId="com.silabs.sdk.stack.super:1.1.1._310456152"/>
|
<storageModule moduleId="com.silabs.ss.framework.ide.project.core.cpp" project.generation="114" projectCommon.boardIds="brd2500a:0.0.0" projectCommon.buildArtifactType="EXE" projectCommon.importModeId="COPY" projectCommon.partId="mcu.arm.efm32.pg1.efm32pg1b200f256gm48" projectCommon.sdkId="com.silabs.sdk.stack.super:1.1.1._310456152"/>
|
||||||
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
|
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
|
||||||
<project id="EFM32.com.silabs.ss.framework.ide.project.core.cdt.cdtMbsProjectType.1696568425" name="SLS CDT Project" projectType="com.silabs.ss.framework.ide.project.core.cdt.cdtMbsProjectType"/>
|
<project id="EFM32.com.silabs.ss.framework.ide.project.core.cdt.cdtMbsProjectType.1696568425" name="SLS CDT Project" projectType="com.silabs.ss.framework.ide.project.core.cdt.cdtMbsProjectType"/>
|
||||||
</storageModule>
|
</storageModule>
|
||||||
|
@ -57,6 +57,11 @@
|
|||||||
<property object="PD10" propertyId="ports.settings.pullup" value="Enabled"/>
|
<property object="PD10" propertyId="ports.settings.pullup" value="Enabled"/>
|
||||||
<property object="PF4" propertyId="ports.settings.pinmode" value="Push-pull"/>
|
<property object="PF4" propertyId="ports.settings.pinmode" value="Push-pull"/>
|
||||||
<property object="PF5" propertyId="ports.settings.pinmode" value="Push-pull"/>
|
<property object="PF5" propertyId="ports.settings.pinmode" value="Push-pull"/>
|
||||||
|
<property object="PF6" propertyId="ports.settings.dout" value="1"/>
|
||||||
|
<property object="PF6" propertyId="ports.settings.filter" value="Enabled"/>
|
||||||
|
<property object="PF6" propertyId="ports.settings.pinmode" value="Input pull"/>
|
||||||
|
<property object="PF6" propertyId="ports.settings.pulldirection" value="Pullup"/>
|
||||||
|
<property object="PF6" propertyId="ports.settings.pullup" value="Enabled"/>
|
||||||
<property object="PORTIO" propertyId="portio.usart0.enable.cts" value="Enabled"/>
|
<property object="PORTIO" propertyId="portio.usart0.enable.cts" value="Enabled"/>
|
||||||
<property object="PORTIO" propertyId="portio.usart0.enable.rts" value="Enabled"/>
|
<property object="PORTIO" propertyId="portio.usart0.enable.rts" value="Enabled"/>
|
||||||
<property object="PORTIO" propertyId="portio.usart0.enable.rx" value="Enabled"/>
|
<property object="PORTIO" propertyId="portio.usart0.enable.rx" value="Enabled"/>
|
||||||
|
@ -651,6 +651,9 @@ extern void PORTIO_enter_DefaultMode_from_RESET(void) {
|
|||||||
|
|
||||||
/* Pin PF5 is configured to Push-pull */
|
/* Pin PF5 is configured to Push-pull */
|
||||||
GPIO_PinModeSet(gpioPortF, 5, gpioModePushPull, 0);
|
GPIO_PinModeSet(gpioPortF, 5, gpioModePushPull, 0);
|
||||||
|
|
||||||
|
/* Pin PF6 is configured to Input enabled with pull-up */
|
||||||
|
GPIO_PinModeSet(gpioPortF, 6, gpioModeInputPull, 1);
|
||||||
// [Port F Configuration]$
|
// [Port F Configuration]$
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,13 @@
|
|||||||
#define STATE1_PAGE 126
|
#define STATE1_PAGE 126
|
||||||
#define STATE2_PAGE 127
|
#define STATE2_PAGE 127
|
||||||
|
|
||||||
|
#define APPLICATION_START_ADDR 0x8000
|
||||||
|
#define APPLICATION_START_PAGE (0x8000/PAGE_SIZE)
|
||||||
|
|
||||||
|
#define APPLICATION_END_ADDR (PAGE_SIZE*125-4) // NOT included in application
|
||||||
|
#define APPLICATION_END_PAGE (125) // 125 is NOT included in application
|
||||||
|
|
||||||
|
#define AUTH_WORD_ADDR (PAGE_SIZE*125-4)
|
||||||
|
|
||||||
static void init_atomic_counter()
|
static void init_atomic_counter()
|
||||||
{
|
{
|
||||||
@ -254,9 +260,6 @@ void init_adc()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
static uint8_t _STATE1[sizeof(AuthenticatorState)];
|
|
||||||
static uint8_t _STATE2[sizeof(AuthenticatorState)];
|
|
||||||
|
|
||||||
void authenticator_read_state(AuthenticatorState * state)
|
void authenticator_read_state(AuthenticatorState * state)
|
||||||
{
|
{
|
||||||
uint32_t * ptr = PAGE_SIZE*STATE1_PAGE;
|
uint32_t * ptr = PAGE_SIZE*STATE1_PAGE;
|
||||||
@ -272,7 +275,6 @@ void authenticator_read_backup_state(AuthenticatorState * state )
|
|||||||
void authenticator_write_state(AuthenticatorState * state, int backup)
|
void authenticator_write_state(AuthenticatorState * state, int backup)
|
||||||
{
|
{
|
||||||
uint32_t * ptr;
|
uint32_t * ptr;
|
||||||
int i;
|
|
||||||
if (! backup)
|
if (! backup)
|
||||||
{
|
{
|
||||||
ptr = PAGE_SIZE*STATE1_PAGE;
|
ptr = PAGE_SIZE*STATE1_PAGE;
|
||||||
@ -402,6 +404,104 @@ void device_init(void)
|
|||||||
buf[i] = adc_rng();
|
buf[i] = adc_rng();
|
||||||
}
|
}
|
||||||
dump_hex(buf,sizeof(buf));
|
dump_hex(buf,sizeof(buf));
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
BootWrite = 0x40,
|
||||||
|
BootDone = 0x41,
|
||||||
|
BootCheck = 0x42,
|
||||||
|
BootErase = 0x43,
|
||||||
|
} WalletOperation;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t op;
|
||||||
|
uint8_t addr[3];
|
||||||
|
uint8_t tag[4];
|
||||||
|
uint8_t len;
|
||||||
|
uint8_t payload[255 - 9];
|
||||||
|
} __attribute__((packed)) BootloaderReq;
|
||||||
|
|
||||||
|
//#define APPLICATION_START_ADDR 0x8000
|
||||||
|
//#define APPLICATION_START_PAGE (0x8000/PAGE_SIZE)
|
||||||
|
|
||||||
|
//#define APPLICATION_END_ADDR (PAGE_SIZE*125-4) // NOT included in application
|
||||||
|
|
||||||
|
static void erase_application()
|
||||||
|
{
|
||||||
|
int page;
|
||||||
|
uint32_t * ptrpage;
|
||||||
|
for(page = APPLICATION_START_PAGE; page < APPLICATION_END_PAGE; page++)
|
||||||
|
{
|
||||||
|
ptrpage = page * PAGE_SIZE;
|
||||||
|
MSC_ErasePage(ptrpage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void authorize_application()
|
||||||
|
{
|
||||||
|
uint32_t zero = 0;
|
||||||
|
uint32_t * ptr;
|
||||||
|
ptr = AUTH_WORD_ADDR;
|
||||||
|
MSC_WriteWordFast(ptr,&zero, 4);
|
||||||
|
}
|
||||||
|
int bootloader_bridge(uint8_t klen, uint8_t * keyh)
|
||||||
|
{
|
||||||
|
static int has_erased = 0;
|
||||||
|
BootloaderReq * req = (BootloaderReq * )keyh;
|
||||||
|
uint8_t payload[256];
|
||||||
|
/*printf("bootloader_bridge\n");*/
|
||||||
|
if (req->len > 255-9)
|
||||||
|
{
|
||||||
|
return CTAP1_ERR_INVALID_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(payload, 0xff, sizeof(payload));
|
||||||
|
memmove(payload, req->payload, req->len);
|
||||||
|
|
||||||
|
uint32_t addr = (*((uint32_t*)req->addr)) & 0xffffff;
|
||||||
|
|
||||||
|
uint32_t * ptr = addr;
|
||||||
|
|
||||||
|
switch(req->op){
|
||||||
|
case BootWrite:
|
||||||
|
/*printf("BootWrite 0x%08x\n", addr);*/
|
||||||
|
if (ptr < APPLICATION_START_ADDR || ptr >= APPLICATION_END_ADDR)
|
||||||
|
{
|
||||||
|
return CTAP2_ERR_NOT_ALLOWED;
|
||||||
|
}
|
||||||
|
if (!has_erased)
|
||||||
|
{
|
||||||
|
erase_application();
|
||||||
|
has_erased = 1;
|
||||||
|
}
|
||||||
|
MSC_WriteWordFast(ptr,payload, req->len + (req->len%4));
|
||||||
|
break;
|
||||||
|
case BootDone:
|
||||||
|
/*printf("BootDone\n");*/
|
||||||
|
authorize_application();
|
||||||
|
NVIC_SystemReset();
|
||||||
|
break;
|
||||||
|
case BootCheck:
|
||||||
|
/*printf("BootCheck\n");*/
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
case BootErase:
|
||||||
|
/*printf("BootErase\n");*/
|
||||||
|
erase_application();
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return CTAP1_ERR_INVALID_COMMAND;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_authorized_to_boot()
|
||||||
|
{
|
||||||
|
uint32_t * auth = AUTH_WORD_ADDR;
|
||||||
|
return *auth == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,6 +29,11 @@
|
|||||||
<folderInfo id="com.silabs.ss.tool.ide.arm.toolchain.gnu.cdt.debug#com.silabs.ss.tool.ide.arm.toolchain.gnu.cdt:7.2.1.20170904." name="/" resourcePath="">
|
<folderInfo id="com.silabs.ss.tool.ide.arm.toolchain.gnu.cdt.debug#com.silabs.ss.tool.ide.arm.toolchain.gnu.cdt:7.2.1.20170904." name="/" resourcePath="">
|
||||||
<toolChain id="com.silabs.ide.si32.gcc.cdt.managedbuild.toolchain.exe.930698941" name="Si32 GNU ARM" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.toolchain.exe">
|
<toolChain id="com.silabs.ide.si32.gcc.cdt.managedbuild.toolchain.exe.930698941" name="Si32 GNU ARM" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.toolchain.exe">
|
||||||
<option id="com.silabs.ide.si32.gcc.cdt.managedbuild.toolchain.debug.level.2037798819" name="Debug Level" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.toolchain.debug.level" value="com.silabs.ide.si32.gcc.cdt.managedbuild.toolchain.debug.level.default" valueType="enumerated"/>
|
<option id="com.silabs.ide.si32.gcc.cdt.managedbuild.toolchain.debug.level.2037798819" name="Debug Level" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.toolchain.debug.level" value="com.silabs.ide.si32.gcc.cdt.managedbuild.toolchain.debug.level.default" valueType="enumerated"/>
|
||||||
|
<option id="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.linker.overrideflash.495029551" name="Override default flash options" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.linker.overrideflash" value="true" valueType="boolean"/>
|
||||||
|
<option id="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.linker.flashlength.2021030475" name="LENGTH" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.linker.flashlength" value="0x8000" valueType="string"/>
|
||||||
|
<option id="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.linker.overrideram.2020332041" name="Override default RAM options" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.linker.overrideram" value="true" valueType="boolean"/>
|
||||||
|
<option id="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.linker.ramorigin.2081162957" name="ORIGIN" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.linker.ramorigin" value="0x20000000" valueType="string"/>
|
||||||
|
<option id="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.linker.ramlength.1981650487" name="LENGTH" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.linker.ramlength" value="0x8000" valueType="string"/>
|
||||||
<targetPlatform binaryParser="org.eclipse.cdt.core.ELF;org.eclipse.cdt.core.GNU_ELF;com.silabs.ss.framework.debugger.core.BIN;com.silabs.ss.framework.debugger.core.HEX;com.silabs.ss.framework.debugger.core.S37;com.silabs.ss.framework.debugger.core.EBL;com.silabs.ss.framework.debugger.core.GBL" id="com.silabs.ide.si32.gcc.cdt.managedbuild.target.gnu.platform.base.613101530" isAbstract="false" name="Debug Platform" osList="win32,linux,macosx" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.target.gnu.platform.base"/>
|
<targetPlatform binaryParser="org.eclipse.cdt.core.ELF;org.eclipse.cdt.core.GNU_ELF;com.silabs.ss.framework.debugger.core.BIN;com.silabs.ss.framework.debugger.core.HEX;com.silabs.ss.framework.debugger.core.S37;com.silabs.ss.framework.debugger.core.EBL;com.silabs.ss.framework.debugger.core.GBL" id="com.silabs.ide.si32.gcc.cdt.managedbuild.target.gnu.platform.base.613101530" isAbstract="false" name="Debug Platform" osList="win32,linux,macosx" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.target.gnu.platform.base"/>
|
||||||
<builder buildPath="${workspace_loc:/efm32boot}/GNU ARM v7.2.1 - Debug" id="com.silabs.ide.si32.gcc.cdt.managedbuild.target.gnu.builder.base.2113865201" keepEnvironmentInBuildfile="false" managedBuildOn="true" name="Si32 GNU ARM Builder" parallelBuildOn="true" parallelizationNumber="optimal" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.target.gnu.builder.base"/>
|
<builder buildPath="${workspace_loc:/efm32boot}/GNU ARM v7.2.1 - Debug" id="com.silabs.ide.si32.gcc.cdt.managedbuild.target.gnu.builder.base.2113865201" keepEnvironmentInBuildfile="false" managedBuildOn="true" name="Si32 GNU ARM Builder" parallelBuildOn="true" parallelizationNumber="optimal" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.target.gnu.builder.base"/>
|
||||||
<tool id="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.compiler.base.981192541" name="GNU ARM C Compiler" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.compiler.base">
|
<tool id="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.compiler.base.981192541" name="GNU ARM C Compiler" superClass="com.silabs.ide.si32.gcc.cdt.managedbuild.tool.gnu.c.compiler.base">
|
||||||
@ -91,9 +96,9 @@
|
|||||||
</folderInfo>
|
</folderInfo>
|
||||||
<sourceEntries>
|
<sourceEntries>
|
||||||
<entry excluding="crypto|fido2|efm32" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/>
|
<entry excluding="crypto|fido2|efm32" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/>
|
||||||
<entry excluding="aes-gcm/|micro-ecc/examples/|micro-ecc/scripts/|micro-ecc/test/|tiny-AES-c/" flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name="crypto"/>
|
<entry excluding="aes-gcm/|micro-ecc/examples/|micro-ecc/scripts/|micro-ecc/test/|tiny-AES-c/" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="crypto"/>
|
||||||
<entry excluding=".settings/|CMSIS/|docs/|emlib/|GNU ARM v7.2.1 - Debug/|hw/|inc/|mbedtls/|sl_crypto/|src/.crypto.c.swp|src/.device.c.swp|src/app.h|src/main.c|.cproject|.project|EFM32.hwconf|Makefile|src/crypto.c" flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name="efm32"/>
|
<entry excluding=".settings/|CMSIS/|docs/|emlib/|GNU ARM v7.2.1 - Debug/|hw/|inc/|mbedtls/|sl_crypto/|src/.crypto.c.swp|src/.device.c.swp|src/app.h|src/main.c|.cproject|.project|EFM32.hwconf|Makefile|src/crypto.c" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="efm32"/>
|
||||||
<entry excluding=".ctap_errors.h.swp|.ctap.c.swp|.ctap.h.swp|.main.c.swp|.storage.h.swp|.wallet.c.swp|.wallet.h.swp|crypto.c|main.c|ctap_parse.c|ctap.c" flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name="fido2"/>
|
<entry excluding=".ctap_errors.h.swp|.ctap.c.swp|.ctap.h.swp|.main.c.swp|.storage.h.swp|.wallet.c.swp|.wallet.h.swp|crypto.c|main.c|ctap_parse.c" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="fido2"/>
|
||||||
</sourceEntries>
|
</sourceEntries>
|
||||||
</configuration>
|
</configuration>
|
||||||
</storageModule>
|
</storageModule>
|
||||||
|
@ -16,7 +16,18 @@
|
|||||||
|
|
||||||
#define BRIDGE_TO_WALLET
|
#define BRIDGE_TO_WALLET
|
||||||
|
|
||||||
|
#define JUMP_LOC 0x8000
|
||||||
|
|
||||||
|
#define PUSH_BUTTON gpioPortF,6
|
||||||
|
|
||||||
|
#define DISABLE_CTAPHID_PING
|
||||||
|
#define DISABLE_CTAPHID_WINK
|
||||||
|
#define DISABLE_CTAPHID_CBOR
|
||||||
|
|
||||||
void printing_init();
|
void printing_init();
|
||||||
|
|
||||||
|
int bootloader_bridge(uint8_t klen, uint8_t * keyh);
|
||||||
|
|
||||||
|
int is_authorized_to_boot();
|
||||||
|
|
||||||
#endif /* SRC_APP_H_ */
|
#endif /* SRC_APP_H_ */
|
||||||
|
98
efm32boot/src/boot.c
Normal file
98
efm32boot/src/boot.c
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/**************************************************************************//**
|
||||||
|
* @file boot.c
|
||||||
|
* @brief Functions for booting another application
|
||||||
|
* @author Silicon Labs
|
||||||
|
* @version 1.03
|
||||||
|
******************************************************************************
|
||||||
|
* @section License
|
||||||
|
* <b>(C) Copyright 2014 Silicon Labs, http://www.silabs.com</b>
|
||||||
|
*******************************************************************************
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely, subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
* claim that you wrote the original software.
|
||||||
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
* misrepresented as being the original software.
|
||||||
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
*
|
||||||
|
* DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no
|
||||||
|
* obligation to support this Software. Silicon Labs is providing the
|
||||||
|
* Software "AS IS", with no express or implied warranties of any kind,
|
||||||
|
* including, but not limited to, any implied warranties of merchantability
|
||||||
|
* or fitness for any particular purpose or warranties against infringement
|
||||||
|
* of any proprietary rights of a third party.
|
||||||
|
*
|
||||||
|
* Silicon Labs will not be liable for any consequential, incidental, or
|
||||||
|
* special damages, or any other relief, or for any claim by any third party,
|
||||||
|
* arising from your use of this Software.
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include "em_device.h"
|
||||||
|
#include "em_gpio.h"
|
||||||
|
#include "em_cmu.h"
|
||||||
|
#include "app.h"
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* This function sets up the Cortex-M3 with a new SP and PC.
|
||||||
|
*****************************************************************************/
|
||||||
|
#if defined ( __CC_ARM )
|
||||||
|
__asm void BOOT_jump(uint32_t sp, uint32_t pc)
|
||||||
|
{
|
||||||
|
/* Set new MSP, PSP based on SP (r0)*/
|
||||||
|
msr msp, r0
|
||||||
|
msr psp, r0
|
||||||
|
|
||||||
|
/* Jump to PC (r1)*/
|
||||||
|
bx r1
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void BOOT_jump(uint32_t sp, uint32_t pc)
|
||||||
|
{
|
||||||
|
(void) sp;
|
||||||
|
(void) pc;
|
||||||
|
/* Set new MSP, PSP based on SP (r0)*/
|
||||||
|
__asm("msr msp, r0");
|
||||||
|
__asm("msr psp, r0");
|
||||||
|
|
||||||
|
/* Jump to PC (r1)*/
|
||||||
|
__asm("mov pc, r1");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Resets any peripherals that have been in use by
|
||||||
|
* the bootloader before booting the appliation */
|
||||||
|
static void resetPeripherals(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* Boots the firmware. This function will activate the vector table
|
||||||
|
* of the firmware application and set the PC and SP from this table.
|
||||||
|
*****************************************************************************/
|
||||||
|
void BOOT_boot(void)
|
||||||
|
{
|
||||||
|
uint32_t pc, sp;
|
||||||
|
|
||||||
|
uint32_t *bootAddress = (uint32_t *)(JUMP_LOC);
|
||||||
|
|
||||||
|
resetPeripherals();
|
||||||
|
|
||||||
|
/* Set new vector table */
|
||||||
|
SCB->VTOR = (uint32_t)bootAddress;
|
||||||
|
|
||||||
|
/* Read new SP and PC from vector table */
|
||||||
|
sp = bootAddress[0];
|
||||||
|
pc = bootAddress[1];
|
||||||
|
|
||||||
|
/* Do a jump by loading the PC and SP into the CPU registers */
|
||||||
|
BOOT_jump(sp, pc);
|
||||||
|
}
|
@ -1,28 +1,106 @@
|
|||||||
#include "em_device.h"
|
#include "em_device.h"
|
||||||
#include "em_chip.h"
|
#include "em_chip.h"
|
||||||
|
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
#include "app.h"
|
#include "app.h"
|
||||||
|
#include "u2f.h"
|
||||||
|
#include "log.h"
|
||||||
#include "InitDevice.h"
|
#include "InitDevice.h"
|
||||||
|
|
||||||
void bootloader_init(void);
|
void bootloader_init(void);
|
||||||
|
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
|
int count = 0;
|
||||||
|
uint32_t t1 = 0;
|
||||||
|
uint32_t t2 = 0;
|
||||||
|
uint32_t accum = 0;
|
||||||
|
uint32_t dt = 0;
|
||||||
|
uint8_t hidmsg[64];
|
||||||
/* Chip errata */
|
/* Chip errata */
|
||||||
CHIP_Init();
|
CHIP_Init();
|
||||||
|
|
||||||
EMU_enter_DefaultMode_from_RESET();
|
EMU_enter_DefaultMode_from_RESET();
|
||||||
CMU_enter_DefaultMode_from_RESET();
|
CMU_enter_DefaultMode_from_RESET();
|
||||||
// ADC0_enter_DefaultMode_from_RESET();
|
// ADC0_enter_DefaultMode_from_RESET();
|
||||||
USART0_enter_DefaultMode_from_RESET();
|
USART0_enter_DefaultMode_from_RESET();
|
||||||
USART1_enter_DefaultMode_from_RESET();
|
USART1_enter_DefaultMode_from_RESET();
|
||||||
LDMA_enter_DefaultMode_from_RESET();
|
// LDMA_enter_DefaultMode_from_RESET();
|
||||||
CRYOTIMER_enter_DefaultMode_from_RESET();
|
CRYOTIMER_enter_DefaultMode_from_RESET();
|
||||||
PORTIO_enter_DefaultMode_from_RESET();
|
PORTIO_enter_DefaultMode_from_RESET();
|
||||||
|
|
||||||
bootloader_init();
|
bootloader_init();
|
||||||
|
|
||||||
|
set_logging_mask(
|
||||||
|
/*0*/
|
||||||
|
TAG_GEN|
|
||||||
|
/*TAG_MC |*/
|
||||||
|
/*TAG_GA |*/
|
||||||
|
/*TAG_WALLET |*/
|
||||||
|
TAG_STOR |
|
||||||
|
/*TAG_CP |*/
|
||||||
|
// TAG_CTAP|
|
||||||
|
/*TAG_HID|*/
|
||||||
|
/*TAG_U2F|*/
|
||||||
|
/*TAG_PARSE |*/
|
||||||
|
// TAG_TIME|
|
||||||
|
/*TAG_DUMP|*/
|
||||||
|
/*TAG_GREEN|*/
|
||||||
|
/*TAG_RED|*/
|
||||||
|
TAG_ERR
|
||||||
|
);
|
||||||
|
|
||||||
|
printf("Bootloader init\r\n");
|
||||||
|
|
||||||
|
if (GPIO_PinInGet(PUSH_BUTTON) == 0)
|
||||||
|
{
|
||||||
|
t1 = millis();
|
||||||
|
while(GPIO_PinInGet(PUSH_BUTTON) == 0 && (millis() - t1) < 2000)
|
||||||
|
;
|
||||||
|
if (GPIO_PinInGet(PUSH_BUTTON) == 0) {
|
||||||
|
bootmode:
|
||||||
|
printf("Reflash condition detected\n");
|
||||||
|
ctaphid_init();
|
||||||
/* Infinite loop */
|
/* Infinite loop */
|
||||||
|
int count = 0;
|
||||||
while (1) {
|
while (1) {
|
||||||
|
if (millis() - t1 > 1000)
|
||||||
|
{
|
||||||
|
/*printf("heartbeat %ld\n", beat++);*/
|
||||||
|
heartbeat();
|
||||||
|
t1 = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (usbhid_recv(hidmsg) > 0)
|
||||||
|
{
|
||||||
|
/*printf("%d>> ",count++); dump_hex1(TAG_DUMP, hidmsg,sizeof(hidmsg));*/
|
||||||
|
// t2 = millis();
|
||||||
|
ctaphid_handle_packet(hidmsg);
|
||||||
|
// accum += millis() - t2;
|
||||||
|
// printf("accum: %d\n", (uint32_t)accum);
|
||||||
|
// printf("dt: %d\n", t2 - dt);
|
||||||
|
// dt = t2;
|
||||||
|
memset(hidmsg, 0, sizeof(hidmsg));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*main_loop_delay();*/
|
||||||
|
}
|
||||||
|
ctaphid_check_timeouts();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Normal boot\n");
|
||||||
|
|
||||||
|
if (is_authorized_to_boot())
|
||||||
|
{
|
||||||
|
BOOT_boot();
|
||||||
|
} else {
|
||||||
|
printf("Warning: not authorized to boot\n");
|
||||||
|
goto bootmode;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "time.h"
|
#include "time.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "app.h"
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
@ -497,10 +498,12 @@ void ctaphid_handle_packet(uint8_t * pkt_raw)
|
|||||||
case BUFFERED:
|
case BUFFERED:
|
||||||
switch(buffer_cmd())
|
switch(buffer_cmd())
|
||||||
{
|
{
|
||||||
|
|
||||||
case CTAPHID_INIT:
|
case CTAPHID_INIT:
|
||||||
printf2(TAG_ERR,"CTAPHID_INIT, error this should already be handled\n");
|
printf2(TAG_ERR,"CTAPHID_INIT, error this should already be handled\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
break;
|
break;
|
||||||
|
#ifndef DISABLE_CTAPHID_PING
|
||||||
case CTAPHID_PING:
|
case CTAPHID_PING:
|
||||||
printf1(TAG_HID,"CTAPHID_PING\n");
|
printf1(TAG_HID,"CTAPHID_PING\n");
|
||||||
|
|
||||||
@ -514,7 +517,8 @@ void ctaphid_handle_packet(uint8_t * pkt_raw)
|
|||||||
t2 = millis();
|
t2 = millis();
|
||||||
printf1(TAG_TIME,"PING writeback: %d ms\n",(uint32_t)(t2-t1));
|
printf1(TAG_TIME,"PING writeback: %d ms\n",(uint32_t)(t2-t1));
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
#ifndef DISABLE_CTAPHID_WINK
|
||||||
case CTAPHID_WINK:
|
case CTAPHID_WINK:
|
||||||
printf1(TAG_HID,"CTAPHID_WINK\n");
|
printf1(TAG_HID,"CTAPHID_WINK\n");
|
||||||
|
|
||||||
@ -526,7 +530,8 @@ void ctaphid_handle_packet(uint8_t * pkt_raw)
|
|||||||
ctaphid_write(&wb,NULL,0);
|
ctaphid_write(&wb,NULL,0);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
#ifndef DISABLE_CTAPHID_CBOR
|
||||||
case CTAPHID_CBOR:
|
case CTAPHID_CBOR:
|
||||||
printf1(TAG_HID,"CTAPHID_CBOR\n");
|
printf1(TAG_HID,"CTAPHID_CBOR\n");
|
||||||
if (buffer_len() == 0)
|
if (buffer_len() == 0)
|
||||||
@ -552,7 +557,7 @@ void ctaphid_handle_packet(uint8_t * pkt_raw)
|
|||||||
t2 = millis();
|
t2 = millis();
|
||||||
printf1(TAG_TIME,"CBOR writeback: %d ms\n",(uint32_t)(t2-t1));
|
printf1(TAG_TIME,"CBOR writeback: %d ms\n",(uint32_t)(t2-t1));
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
case CTAPHID_MSG:
|
case CTAPHID_MSG:
|
||||||
printf1(TAG_HID,"CTAPHID_MSG\n");
|
printf1(TAG_HID,"CTAPHID_MSG\n");
|
||||||
if (buffer_len() == 0)
|
if (buffer_len() == 0)
|
||||||
|
@ -31,8 +31,10 @@ void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp)
|
|||||||
rcode = U2F_SW_CLASS_NOT_SUPPORTED;
|
rcode = U2F_SW_CLASS_NOT_SUPPORTED;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
#ifdef BRIDGE_TO_WALLET
|
#if defined(BRIDGE_TO_WALLET)
|
||||||
struct u2f_authenticate_request * auth = (struct u2f_authenticate_request *) req->payload;
|
struct u2f_authenticate_request * auth = (struct u2f_authenticate_request *) req->payload;
|
||||||
|
|
||||||
|
|
||||||
if (req->ins == U2F_AUTHENTICATE)
|
if (req->ins == U2F_AUTHENTICATE)
|
||||||
{
|
{
|
||||||
if (req->p1 == U2F_AUTHENTICATE_CHECK)
|
if (req->p1 == U2F_AUTHENTICATE_CHECK)
|
||||||
@ -77,6 +79,7 @@ void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp)
|
|||||||
{
|
{
|
||||||
rcode = U2F_SW_INS_NOT_SUPPORTED;
|
rcode = U2F_SW_INS_NOT_SUPPORTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
switch(req->ins)
|
switch(req->ins)
|
||||||
{
|
{
|
||||||
|
@ -200,12 +200,6 @@ int16_t bridge_u2f_to_wallet(uint8_t * _chal, uint8_t * _appid, uint8_t klen, ui
|
|||||||
|
|
||||||
memset(lens,0,sizeof(lens));
|
memset(lens,0,sizeof(lens));
|
||||||
|
|
||||||
|
|
||||||
for (i = 0; i < sizeof(sig); i++)
|
|
||||||
{
|
|
||||||
sig[i] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
wallet_request * req = (wallet_request *) msg_buf;
|
wallet_request * req = (wallet_request *) msg_buf;
|
||||||
uint8_t * payload = req->payload;
|
uint8_t * payload = req->payload;
|
||||||
|
|
||||||
@ -219,12 +213,15 @@ int16_t bridge_u2f_to_wallet(uint8_t * _chal, uint8_t * _appid, uint8_t klen, ui
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
count = 0;
|
count = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
u2f_response_writeback(&up,1);
|
u2f_response_writeback(&up,1);
|
||||||
u2f_response_writeback((uint8_t *)&count,4);
|
u2f_response_writeback((uint8_t *)&count,4);
|
||||||
u2f_response_writeback((uint8_t *)&ret,1);
|
u2f_response_writeback((uint8_t *)&ret,1);
|
||||||
|
|
||||||
|
#ifndef IS_BOOTLOADER
|
||||||
|
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
for (i = 0; i < MIN(5,req->numArgs); i++)
|
for (i = 0; i < MIN(5,req->numArgs); i++)
|
||||||
{
|
{
|
||||||
@ -454,6 +451,9 @@ int16_t bridge_u2f_to_wallet(uint8_t * _chal, uint8_t * _appid, uint8_t klen, ui
|
|||||||
ret = CTAP1_ERR_INVALID_COMMAND;
|
ret = CTAP1_ERR_INVALID_COMMAND;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
ret = bootloader_bridge(klen, keyh);
|
||||||
|
#endif
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
|
@ -15,7 +15,7 @@ import socket,json,base64,ssl
|
|||||||
httpport = 8080
|
httpport = 8080
|
||||||
udpport = 8111
|
udpport = 8111
|
||||||
|
|
||||||
|
HEX_FILE = '../efm32/GNU ARM v7.2.1 - Debug/EFM32.hex'
|
||||||
|
|
||||||
def ForceU2F(client,device):
|
def ForceU2F(client,device):
|
||||||
client.ctap = CTAP1(device)
|
client.ctap = CTAP1(device)
|
||||||
@ -25,6 +25,7 @@ def ForceU2F(client,device):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
try:
|
||||||
dev = next(CtapHidDevice.list_devices(), None)
|
dev = next(CtapHidDevice.list_devices(), None)
|
||||||
print(dev)
|
print(dev)
|
||||||
if not dev:
|
if not dev:
|
||||||
@ -32,6 +33,8 @@ if __name__ == '__main__':
|
|||||||
client = Fido2Client(dev, 'https://example.com')
|
client = Fido2Client(dev, 'https://example.com')
|
||||||
ForceU2F(client, dev)
|
ForceU2F(client, dev)
|
||||||
ctap = client.ctap
|
ctap = client.ctap
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
|
||||||
def to_websafe(data):
|
def to_websafe(data):
|
||||||
@ -107,10 +110,15 @@ class UDPBridge(BaseHTTPRequestHandler):
|
|||||||
def do_GET(self):
|
def do_GET(self):
|
||||||
self.send_response(200)
|
self.send_response(200)
|
||||||
self.send_header('Content-type','text/json')
|
self.send_header('Content-type','text/json')
|
||||||
self.end_headers()
|
|
||||||
|
|
||||||
|
h = open(HEX_FILE,'r').read()
|
||||||
|
h = base64.b64encode(h.encode())
|
||||||
|
h = to_websafe(h.decode())
|
||||||
|
sig = [1,2,3,4]
|
||||||
#msg = {'data': read()}
|
#msg = {'data': read()}
|
||||||
msg = {'data': 'rest'};
|
msg = {'firmware': h, 'signature':sig}
|
||||||
|
|
||||||
|
self.end_headers()
|
||||||
|
|
||||||
self.wfile.write(json.dumps(msg).encode())
|
self.wfile.write(json.dumps(msg).encode())
|
||||||
|
|
||||||
|
@ -3,10 +3,12 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>U2F Bridge Demo</h1>
|
<h1>U2F Bridge Demo</h1>
|
||||||
|
<h3 id="progress"></h3>
|
||||||
</body>
|
</body>
|
||||||
<script src="js/u2f-api.js"></script>
|
<script src="js/u2f-api.js"></script>
|
||||||
<script src="js/elliptic.js"></script>
|
<script src="js/elliptic.js"></script>
|
||||||
<script src="js/sha256.min.js"></script>
|
<script src="js/sha256.min.js"></script>
|
||||||
<script src="js/aes.js"></script>
|
<script src="js/aes.js"></script>
|
||||||
|
<script src="js/intel-hex.js"></script>
|
||||||
<script src="js/wallet.js"></script>
|
<script src="js/wallet.js"></script>
|
||||||
</html>
|
</html>
|
||||||
|
977
web/js/intel-hex.js
Normal file
977
web/js/intel-hex.js
Normal file
@ -0,0 +1,977 @@
|
|||||||
|
(function (global, factory) {
|
||||||
|
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||||||
|
typeof define === 'function' && define.amd ? define(factory) :
|
||||||
|
(global.MemoryMap = factory());
|
||||||
|
}(this, (function () { 'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parser/writer for the "Intel hex" format.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A regexp that matches lines in a .hex file.
|
||||||
|
*
|
||||||
|
* One hexadecimal character is matched by "[0-9A-Fa-f]".
|
||||||
|
* Two hex characters are matched by "[0-9A-Fa-f]{2}"
|
||||||
|
* Eight or more hex characters are matched by "[0-9A-Fa-f]{8,}"
|
||||||
|
* A capture group of two hex characters is "([0-9A-Fa-f]{2})"
|
||||||
|
*
|
||||||
|
* Record mark :
|
||||||
|
* 8 or more hex chars ([0-9A-Fa-f]{8,})
|
||||||
|
* Checksum ([0-9A-Fa-f]{2})
|
||||||
|
* Optional newline (?:\r\n|\r|\n|)
|
||||||
|
*/
|
||||||
|
var hexLineRegexp = /:([0-9A-Fa-f]{8,})([0-9A-Fa-f]{2})(?:\r\n|\r|\n|)/g;
|
||||||
|
|
||||||
|
|
||||||
|
// Takes a Uint8Array as input,
|
||||||
|
// Returns an integer in the 0-255 range.
|
||||||
|
function checksum(bytes) {
|
||||||
|
return (-bytes.reduce(function (sum, v){ return sum + v; }, 0)) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Takes two Uint8Arrays as input,
|
||||||
|
// Returns an integer in the 0-255 range.
|
||||||
|
function checksumTwo(array1, array2) {
|
||||||
|
var partial1 = array1.reduce(function (sum, v){ return sum + v; }, 0);
|
||||||
|
var partial2 = array2.reduce(function (sum, v){ return sum + v; }, 0);
|
||||||
|
return -( partial1 + partial2 ) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Trivial utility. Converts a number to hex and pads with zeroes up to 2 characters.
|
||||||
|
function hexpad(number) {
|
||||||
|
return number.toString(16).toUpperCase().padStart(2, '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Polyfill as per https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger
|
||||||
|
Number.isInteger = Number.isInteger || function(value) {
|
||||||
|
return typeof value === 'number' &&
|
||||||
|
isFinite(value) &&
|
||||||
|
Math.floor(value) === value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class MemoryMap
|
||||||
|
*
|
||||||
|
* Represents the contents of a memory layout, with main focus into (possibly sparse) blocks of data.
|
||||||
|
*<br/>
|
||||||
|
* A {@linkcode MemoryMap} acts as a subclass of
|
||||||
|
* {@linkcode https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map|Map}.
|
||||||
|
* In every entry of it, the key is the starting address of a data block (an integer number),
|
||||||
|
* and the value is the <tt>Uint8Array</tt> with the data for that block.
|
||||||
|
*<br/>
|
||||||
|
* The main rationale for this is that a .hex file can contain a single block of contiguous
|
||||||
|
* data starting at memory address 0 (and it's the common case for simple .hex files),
|
||||||
|
* but complex files with several non-contiguous data blocks are also possible, thus
|
||||||
|
* the need for a data structure on top of the <tt>Uint8Array</tt>s.
|
||||||
|
*<br/>
|
||||||
|
* In order to parse <tt>.hex</tt> files, use the {@linkcode MemoryMap.fromHex} <em>static</em> factory
|
||||||
|
* method. In order to write <tt>.hex</tt> files, create a new {@linkcode MemoryMap} and call
|
||||||
|
* its {@linkcode MemoryMap.asHexString} method.
|
||||||
|
*
|
||||||
|
* @extends Map
|
||||||
|
* @example
|
||||||
|
* import MemoryMap from 'nrf-intel-hex';
|
||||||
|
*
|
||||||
|
* let memMap1 = new MemoryMap();
|
||||||
|
* let memMap2 = new MemoryMap([[0, new Uint8Array(1,2,3,4)]]);
|
||||||
|
* let memMap3 = new MemoryMap({0: new Uint8Array(1,2,3,4)});
|
||||||
|
* let memMap4 = new MemoryMap({0xCF0: new Uint8Array(1,2,3,4)});
|
||||||
|
*/
|
||||||
|
var MemoryMap = function MemoryMap(blocks) {
|
||||||
|
var this$1 = this;
|
||||||
|
|
||||||
|
this._blocks = new Map();
|
||||||
|
|
||||||
|
if (blocks && typeof blocks[Symbol.iterator] === 'function') {
|
||||||
|
for (var tuple of blocks) {
|
||||||
|
if (!(tuple instanceof Array) || tuple.length !== 2) {
|
||||||
|
throw new Error('First parameter to MemoryMap constructor must be an iterable of [addr, bytes] or undefined');
|
||||||
|
}
|
||||||
|
this$1.set(tuple[0], tuple[1]);
|
||||||
|
}
|
||||||
|
} else if (typeof blocks === 'object') {
|
||||||
|
// Try iterating through the object's keys
|
||||||
|
var addrs = Object.keys(blocks);
|
||||||
|
for (var addr of addrs) {
|
||||||
|
this$1.set(parseInt(addr), blocks[addr]);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (blocks !== undefined && blocks !== null) {
|
||||||
|
throw new Error('First parameter to MemoryMap constructor must be an iterable of [addr, bytes] or undefined');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var prototypeAccessors = { size: { configurable: true } };
|
||||||
|
|
||||||
|
MemoryMap.prototype.set = function set (addr, value) {
|
||||||
|
if (!Number.isInteger(addr)) {
|
||||||
|
throw new Error('Address passed to MemoryMap is not an integer');
|
||||||
|
}
|
||||||
|
if (addr < 0) {
|
||||||
|
throw new Error('Address passed to MemoryMap is negative');
|
||||||
|
}
|
||||||
|
if (!(value instanceof Uint8Array)) {
|
||||||
|
throw new Error('Bytes passed to MemoryMap are not an Uint8Array');
|
||||||
|
}
|
||||||
|
return this._blocks.set(addr, value);
|
||||||
|
};
|
||||||
|
// Delegate the following to the 'this._blocks' Map:
|
||||||
|
MemoryMap.prototype.get = function get (addr){ return this._blocks.get(addr);};
|
||||||
|
MemoryMap.prototype.clear = function clear () { return this._blocks.clear(); };
|
||||||
|
MemoryMap.prototype.delete = function delete$1 (addr) { return this._blocks.delete(addr); };
|
||||||
|
MemoryMap.prototype.entries = function entries (){ return this._blocks.entries();};
|
||||||
|
MemoryMap.prototype.forEach = function forEach (callback, that) { return this._blocks.forEach(callback, that); };
|
||||||
|
MemoryMap.prototype.has = function has (addr){ return this._blocks.has(addr);};
|
||||||
|
MemoryMap.prototype.keys = function keys () { return this._blocks.keys(); };
|
||||||
|
MemoryMap.prototype.values = function values () { return this._blocks.values(); };
|
||||||
|
prototypeAccessors.size.get = function () { return this._blocks.size; };
|
||||||
|
MemoryMap.prototype[Symbol.iterator] = function () { return this._blocks[Symbol.iterator](); };
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a string containing data formatted in "Intel HEX" format, and
|
||||||
|
* returns an instance of {@linkcode MemoryMap}.
|
||||||
|
*<br/>
|
||||||
|
* The insertion order of keys in the {@linkcode MemoryMap} is guaranteed to be strictly
|
||||||
|
* ascending. In other words, when iterating through the {@linkcode MemoryMap}, the addresses
|
||||||
|
* will be ordered in ascending order.
|
||||||
|
*<br/>
|
||||||
|
* The parser has an opinionated behaviour, and will throw a descriptive error if it
|
||||||
|
* encounters some malformed input. Check the project's
|
||||||
|
* {@link https://github.com/NordicSemiconductor/nrf-intel-hex#Features|README file} for details.
|
||||||
|
*<br/>
|
||||||
|
* If <tt>maxBlockSize</tt> is given, any contiguous data block larger than that will
|
||||||
|
* be split in several blocks.
|
||||||
|
*
|
||||||
|
* @param {String} hexText The contents of a .hex file.
|
||||||
|
* @param {Number} [maxBlockSize=Infinity] Maximum size of the returned <tt>Uint8Array</tt>s.
|
||||||
|
*
|
||||||
|
* @return {MemoryMap}
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* import MemoryMap from 'nrf-intel-hex';
|
||||||
|
*
|
||||||
|
* let intelHexString =
|
||||||
|
* ":100000000102030405060708090A0B0C0D0E0F1068\n" +
|
||||||
|
* ":00000001FF";
|
||||||
|
*
|
||||||
|
* let memMap = MemoryMap.fromHex(intelHexString);
|
||||||
|
*
|
||||||
|
* for (let [address, dataBlock] of memMap) {
|
||||||
|
* console.log('Data block at ', address, ', bytes: ', dataBlock);
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
MemoryMap.fromHex = function fromHex (hexText, maxBlockSize) {
|
||||||
|
if ( maxBlockSize === void 0 ) maxBlockSize = Infinity;
|
||||||
|
|
||||||
|
var blocks = new MemoryMap();
|
||||||
|
|
||||||
|
var lastCharacterParsed = 0;
|
||||||
|
var matchResult;
|
||||||
|
var recordCount = 0;
|
||||||
|
|
||||||
|
// Upper Linear Base Address, the 16 most significant bits (2 bytes) of
|
||||||
|
// the current 32-bit (4-byte) address
|
||||||
|
// In practice this is a offset that is summed to the "load offset" of the
|
||||||
|
// data records
|
||||||
|
var ulba = 0;
|
||||||
|
|
||||||
|
hexLineRegexp.lastIndex = 0; // Reset the regexp, if not it would skip content when called twice
|
||||||
|
|
||||||
|
while ((matchResult = hexLineRegexp.exec(hexText)) !== null) {
|
||||||
|
recordCount++;
|
||||||
|
|
||||||
|
// By default, a regexp loop ignores gaps between matches, but
|
||||||
|
// we want to be aware of them.
|
||||||
|
if (lastCharacterParsed !== matchResult.index) {
|
||||||
|
throw new Error(
|
||||||
|
'Malformed hex file: Could not parse between characters ' +
|
||||||
|
lastCharacterParsed +
|
||||||
|
' and ' +
|
||||||
|
matchResult.index +
|
||||||
|
' ("' +
|
||||||
|
hexText.substring(lastCharacterParsed, Math.min(matchResult.index, lastCharacterParsed + 16)).trim() +
|
||||||
|
'")');
|
||||||
|
}
|
||||||
|
lastCharacterParsed = hexLineRegexp.lastIndex;
|
||||||
|
|
||||||
|
// Give pretty names to the match's capture groups
|
||||||
|
var recordStr = matchResult[1];
|
||||||
|
var recordChecksum = matchResult[2];
|
||||||
|
|
||||||
|
// String to Uint8Array - https://stackoverflow.com/questions/43131242/how-to-convert-a-hexademical-string-of-data-to-an-arraybuffer-in-javascript
|
||||||
|
var recordBytes = new Uint8Array(recordStr.match(/[\da-f]{2}/gi).map(function (h){ return parseInt(h, 16); }));
|
||||||
|
|
||||||
|
var recordLength = recordBytes[0];
|
||||||
|
if (recordLength + 4 !== recordBytes.length) {
|
||||||
|
throw new Error('Mismatched record length at record ' + recordCount + ' (' + matchResult[0].trim() + '), expected ' + (recordLength) + ' data bytes but actual length is ' + (recordBytes.length - 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
var cs = checksum(recordBytes);
|
||||||
|
if (parseInt(recordChecksum, 16) !== cs) {
|
||||||
|
throw new Error('Checksum failed at record ' + recordCount + ' (' + matchResult[0].trim() + '), should be ' + cs.toString(16) );
|
||||||
|
}
|
||||||
|
|
||||||
|
var offset = (recordBytes[1] << 8) + recordBytes[2];
|
||||||
|
var recordType = recordBytes[3];
|
||||||
|
var data = recordBytes.subarray(4);
|
||||||
|
|
||||||
|
if (recordType === 0) {
|
||||||
|
// Data record, contains data
|
||||||
|
// Create a new block, at (upper linear base address + offset)
|
||||||
|
if (blocks.has(ulba + offset)) {
|
||||||
|
throw new Error('Duplicated data at record ' + recordCount + ' (' + matchResult[0].trim() + ')');
|
||||||
|
}
|
||||||
|
if (offset + data.length > 0x10000) {
|
||||||
|
throw new Error(
|
||||||
|
'Data at record ' +
|
||||||
|
recordCount +
|
||||||
|
' (' +
|
||||||
|
matchResult[0].trim() +
|
||||||
|
') wraps over 0xFFFF. This would trigger ambiguous behaviour. Please restructure your data so that for every record the data offset plus the data length do not exceed 0xFFFF.');
|
||||||
|
}
|
||||||
|
|
||||||
|
blocks.set( ulba + offset, data );
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// All non-data records must have a data offset of zero
|
||||||
|
if (offset !== 0) {
|
||||||
|
throw new Error('Record ' + recordCount + ' (' + matchResult[0].trim() + ') must have 0000 as data offset.');
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (recordType) {
|
||||||
|
case 1: // EOF
|
||||||
|
if (lastCharacterParsed !== hexText.length) {
|
||||||
|
// This record should be at the very end of the string
|
||||||
|
throw new Error('There is data after an EOF record at record ' + recordCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
return blocks.join(maxBlockSize);
|
||||||
|
|
||||||
|
case 2: // Extended Segment Address Record
|
||||||
|
// Sets the 16 most significant bits of the 20-bit Segment Base
|
||||||
|
// Address for the subsequent data.
|
||||||
|
ulba = ((data[0] << 8) + data[1]) << 4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3: // Start Segment Address Record
|
||||||
|
// Do nothing. Record type 3 only applies to 16-bit Intel CPUs,
|
||||||
|
// where it should reset the program counter (CS+IP CPU registers)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4: // Extended Linear Address Record
|
||||||
|
// Sets the 16 most significant (upper) bits of the 32-bit Linear Address
|
||||||
|
// for the subsequent data
|
||||||
|
ulba = ((data[0] << 8) + data[1]) << 16;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 5: // Start Linear Address Record
|
||||||
|
// Do nothing. Record type 5 only applies to 32-bit Intel CPUs,
|
||||||
|
// where it should reset the program counter (EIP CPU register)
|
||||||
|
// It might have meaning for other CPU architectures
|
||||||
|
// (see http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka9903.html )
|
||||||
|
// but will be ignored nonetheless.
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error('Invalid record type 0x' + hexpad(recordType) + ' at record ' + recordCount + ' (should be between 0x00 and 0x05)');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recordCount) {
|
||||||
|
throw new Error('No EOF record at end of file');
|
||||||
|
} else {
|
||||||
|
throw new Error('Malformed .hex file, could not parse any registers');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a <strong>new</strong> instance of {@linkcode MemoryMap}, containing
|
||||||
|
* the same data, but concatenating together those memory blocks that are adjacent.
|
||||||
|
*<br/>
|
||||||
|
* The insertion order of keys in the {@linkcode MemoryMap} is guaranteed to be strictly
|
||||||
|
* ascending. In other words, when iterating through the {@linkcode MemoryMap}, the addresses
|
||||||
|
* will be ordered in ascending order.
|
||||||
|
*<br/>
|
||||||
|
* If <tt>maxBlockSize</tt> is given, blocks will be concatenated together only
|
||||||
|
* until the joined block reaches this size in bytes. This means that the output
|
||||||
|
* {@linkcode MemoryMap} might have more entries than the input one.
|
||||||
|
*<br/>
|
||||||
|
* If there is any overlap between blocks, an error will be thrown.
|
||||||
|
*<br/>
|
||||||
|
* The returned {@linkcode MemoryMap} will use newly allocated memory.
|
||||||
|
*
|
||||||
|
* @param {Number} [maxBlockSize=Infinity] Maximum size of the <tt>Uint8Array</tt>s in the
|
||||||
|
* returned {@linkcode MemoryMap}.
|
||||||
|
*
|
||||||
|
* @return {MemoryMap}
|
||||||
|
*/
|
||||||
|
MemoryMap.prototype.join = function join (maxBlockSize) {
|
||||||
|
var this$1 = this;
|
||||||
|
if ( maxBlockSize === void 0 ) maxBlockSize = Infinity;
|
||||||
|
|
||||||
|
|
||||||
|
// First pass, create a Map of address→length of contiguous blocks
|
||||||
|
var sortedKeys = Array.from(this.keys()).sort(function (a,b){ return a-b; });
|
||||||
|
var blockSizes = new Map();
|
||||||
|
var lastBlockAddr = -1;
|
||||||
|
var lastBlockEndAddr = -1;
|
||||||
|
|
||||||
|
for (var i=0,l=sortedKeys.length; i<l; i++) {
|
||||||
|
var blockAddr = sortedKeys[i];
|
||||||
|
var blockLength = this$1.get(sortedKeys[i]).length;
|
||||||
|
|
||||||
|
if (lastBlockEndAddr === blockAddr && (lastBlockEndAddr - lastBlockAddr) < maxBlockSize) {
|
||||||
|
// Grow when the previous end address equals the current,
|
||||||
|
// and we don't go over the maximum block size.
|
||||||
|
blockSizes.set(lastBlockAddr, blockSizes.get(lastBlockAddr) + blockLength);
|
||||||
|
lastBlockEndAddr += blockLength;
|
||||||
|
} else if (lastBlockEndAddr <= blockAddr) {
|
||||||
|
// Else mark a new block.
|
||||||
|
blockSizes.set(blockAddr, blockLength);
|
||||||
|
lastBlockAddr = blockAddr;
|
||||||
|
lastBlockEndAddr = blockAddr + blockLength;
|
||||||
|
} else {
|
||||||
|
throw new Error('Overlapping data around address 0x' + blockAddr.toString(16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second pass: allocate memory for the contiguous blocks and copy data around.
|
||||||
|
var mergedBlocks = new MemoryMap();
|
||||||
|
var mergingBlock;
|
||||||
|
var mergingBlockAddr = -1;
|
||||||
|
for (var i$1=0,l$1=sortedKeys.length; i$1<l$1; i$1++) {
|
||||||
|
var blockAddr$1 = sortedKeys[i$1];
|
||||||
|
if (blockSizes.has(blockAddr$1)) {
|
||||||
|
mergingBlock = new Uint8Array(blockSizes.get(blockAddr$1));
|
||||||
|
mergedBlocks.set(blockAddr$1, mergingBlock);
|
||||||
|
mergingBlockAddr = blockAddr$1;
|
||||||
|
}
|
||||||
|
mergingBlock.set(this$1.get(blockAddr$1), blockAddr$1 - mergingBlockAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mergedBlocks;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map|<tt>Map</tt>}
|
||||||
|
* of {@linkcode MemoryMap}s, indexed by a alphanumeric ID,
|
||||||
|
* returns a <tt>Map</tt> of address to tuples (<tt>Arrays</tt>s of length 2) of the form
|
||||||
|
* <tt>(id, Uint8Array)</tt>s.
|
||||||
|
*<br/>
|
||||||
|
* The scenario for using this is having several {@linkcode MemoryMap}s, from several calls to
|
||||||
|
* {@link module:nrf-intel-hex~hexToArrays|hexToArrays}, each having a different identifier.
|
||||||
|
* This function locates where those memory block sets overlap, and returns a <tt>Map</tt>
|
||||||
|
* containing addresses as keys, and arrays as values. Each array will contain 1 or more
|
||||||
|
* <tt>(id, Uint8Array)</tt> tuples: the identifier of the memory block set that has
|
||||||
|
* data in that region, and the data itself. When memory block sets overlap, there will
|
||||||
|
* be more than one tuple.
|
||||||
|
*<br/>
|
||||||
|
* The <tt>Uint8Array</tt>s in the output are
|
||||||
|
* {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/subarray|subarrays}
|
||||||
|
* of the input data; new memory is <strong>not</strong> allocated for them.
|
||||||
|
*<br/>
|
||||||
|
* The insertion order of keys in the output <tt>Map</tt> is guaranteed to be strictly
|
||||||
|
* ascending. In other words, when iterating through the <tt>Map</tt>, the addresses
|
||||||
|
* will be ordered in ascending order.
|
||||||
|
*<br/>
|
||||||
|
* When two blocks overlap, the corresponding array of tuples will have the tuples ordered
|
||||||
|
* in the insertion order of the input <tt>Map</tt> of block sets.
|
||||||
|
*<br/>
|
||||||
|
*
|
||||||
|
* @param {Map.MemoryMap} memoryMaps The input memory block sets
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* import MemoryMap from 'nrf-intel-hex';
|
||||||
|
*
|
||||||
|
* let memMap1 = MemoryMap.fromHex( hexdata1 );
|
||||||
|
* let memMap2 = MemoryMap.fromHex( hexdata2 );
|
||||||
|
* let memMap3 = MemoryMap.fromHex( hexdata3 );
|
||||||
|
*
|
||||||
|
* let maps = new Map([
|
||||||
|
* ['file A', blocks1],
|
||||||
|
* ['file B', blocks2],
|
||||||
|
* ['file C', blocks3]
|
||||||
|
* ]);
|
||||||
|
*
|
||||||
|
* let overlappings = MemoryMap.overlapMemoryMaps(maps);
|
||||||
|
*
|
||||||
|
* for (let [address, tuples] of overlappings) {
|
||||||
|
* // if 'tuples' has length > 1, there is an overlap starting at 'address'
|
||||||
|
*
|
||||||
|
* for (let [address, tuples] of overlappings) {
|
||||||
|
* let [id, bytes] = tuple;
|
||||||
|
* // 'id' in this example is either 'file A', 'file B' or 'file C'
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* @return {Map.Array<mixed,Uint8Array>} The map of possibly overlapping memory blocks
|
||||||
|
*/
|
||||||
|
MemoryMap.overlapMemoryMaps = function overlapMemoryMaps (memoryMaps) {
|
||||||
|
// First pass: create a list of addresses where any block starts or ends.
|
||||||
|
var cuts = new Set();
|
||||||
|
for (var [, blocks] of memoryMaps) {
|
||||||
|
for (var [address, block] of blocks) {
|
||||||
|
cuts.add(address);
|
||||||
|
cuts.add(address + block.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var orderedCuts = Array.from(cuts.values()).sort(function (a,b){ return a-b; });
|
||||||
|
var overlaps = new Map();
|
||||||
|
|
||||||
|
// Second pass: iterate through the cuts, get slices of every intersecting blockset
|
||||||
|
var loop = function ( i, l ) {
|
||||||
|
var cut = orderedCuts[i];
|
||||||
|
var nextCut = orderedCuts[i+1];
|
||||||
|
var tuples = [];
|
||||||
|
|
||||||
|
for (var [setId, blocks$1] of memoryMaps) {
|
||||||
|
// Find the block with the highest address that is equal or lower to
|
||||||
|
// the current cut (if any)
|
||||||
|
var blockAddr = Array.from(blocks$1.keys()).reduce(function (acc, val){
|
||||||
|
if (val > cut) {
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
return Math.max( acc, val );
|
||||||
|
}, -1);
|
||||||
|
|
||||||
|
if (blockAddr !== -1) {
|
||||||
|
var block$1 = blocks$1.get(blockAddr);
|
||||||
|
var subBlockStart = cut - blockAddr;
|
||||||
|
var subBlockEnd = nextCut - blockAddr;
|
||||||
|
|
||||||
|
if (subBlockStart < block$1.length) {
|
||||||
|
tuples.push([ setId, block$1.subarray(subBlockStart, subBlockEnd) ]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tuples.length) {
|
||||||
|
overlaps.set(cut, tuples);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var i=0, l=orderedCuts.length-1; i<l; i++) loop( i, l );
|
||||||
|
|
||||||
|
return overlaps;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given the output of the {@linkcode MemoryMap.overlapMemoryMaps|overlapMemoryMaps}
|
||||||
|
* (a <tt>Map</tt> of address to an <tt>Array</tt> of <tt>(id, Uint8Array)</tt> tuples),
|
||||||
|
* returns a {@linkcode MemoryMap}. This discards the IDs in the process.
|
||||||
|
*<br/>
|
||||||
|
* The output <tt>Map</tt> contains as many entries as the input one (using the same addresses
|
||||||
|
* as keys), but the value for each entry will be the <tt>Uint8Array</tt> of the <b>last</b>
|
||||||
|
* tuple for each address in the input data.
|
||||||
|
*<br/>
|
||||||
|
* The scenario is wanting to join together several parsed .hex files, not worrying about
|
||||||
|
* their overlaps.
|
||||||
|
*<br/>
|
||||||
|
*
|
||||||
|
* @param {Map.Array<mixed,Uint8Array>} overlaps The (possibly overlapping) input memory blocks
|
||||||
|
* @return {MemoryMap} The flattened memory blocks
|
||||||
|
*/
|
||||||
|
MemoryMap.flattenOverlaps = function flattenOverlaps (overlaps) {
|
||||||
|
return new MemoryMap(
|
||||||
|
Array.from(overlaps.entries()).map(function (ref) {
|
||||||
|
var address = ref[0];
|
||||||
|
var tuples = ref[1];
|
||||||
|
|
||||||
|
return [address, tuples[tuples.length - 1][1] ];
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new instance of {@linkcode MemoryMap}, where:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Each key (the start address of each <tt>Uint8Array</tt>) is a multiple of
|
||||||
|
*<tt>pageSize</tt></li>
|
||||||
|
* <li>The size of each <tt>Uint8Array</tt> is exactly <tt>pageSize</tt></li>
|
||||||
|
* <li>Bytes from the input map to bytes in the output</li>
|
||||||
|
* <li>Bytes not in the input are replaced by a padding value</li>
|
||||||
|
* </ul>
|
||||||
|
*<br/>
|
||||||
|
* The scenario is wanting to prepare pages of bytes for a write operation, where the write
|
||||||
|
* operation affects a whole page/sector at once.
|
||||||
|
*<br/>
|
||||||
|
* The insertion order of keys in the output {@linkcode MemoryMap} is guaranteed
|
||||||
|
* to be strictly ascending. In other words, when iterating through the
|
||||||
|
* {@linkcode MemoryMap}, the addresses will be ordered in ascending order.
|
||||||
|
*<br/>
|
||||||
|
* The <tt>Uint8Array</tt>s in the output will be newly allocated.
|
||||||
|
*<br/>
|
||||||
|
*
|
||||||
|
* @param {Number} [pageSize=1024] The size of the output pages, in bytes
|
||||||
|
* @param {Number} [pad=0xFF] The byte value to use for padding
|
||||||
|
* @return {MemoryMap}
|
||||||
|
*/
|
||||||
|
MemoryMap.prototype.paginate = function paginate ( pageSize, pad) {
|
||||||
|
var this$1 = this;
|
||||||
|
if ( pageSize === void 0 ) pageSize=1024;
|
||||||
|
if ( pad === void 0 ) pad=0xFF;
|
||||||
|
|
||||||
|
if (pageSize <= 0) {
|
||||||
|
throw new Error('Page size must be greater than zero');
|
||||||
|
}
|
||||||
|
var outPages = new MemoryMap();
|
||||||
|
var page;
|
||||||
|
|
||||||
|
var sortedKeys = Array.from(this.keys()).sort(function (a,b){ return a-b; });
|
||||||
|
|
||||||
|
for (var i=0,l=sortedKeys.length; i<l; i++) {
|
||||||
|
var blockAddr = sortedKeys[i];
|
||||||
|
var block = this$1.get(blockAddr);
|
||||||
|
var blockLength = block.length;
|
||||||
|
var blockEnd = blockAddr + blockLength;
|
||||||
|
|
||||||
|
for (var pageAddr = blockAddr - (blockAddr % pageSize); pageAddr < blockEnd; pageAddr += pageSize) {
|
||||||
|
page = outPages.get(pageAddr);
|
||||||
|
if (!page) {
|
||||||
|
page = new Uint8Array(pageSize);
|
||||||
|
page.fill(pad);
|
||||||
|
outPages.set(pageAddr, page);
|
||||||
|
}
|
||||||
|
|
||||||
|
var offset = pageAddr - blockAddr;
|
||||||
|
var subBlock = (void 0);
|
||||||
|
if (offset <= 0) {
|
||||||
|
// First page which intersects the block
|
||||||
|
subBlock = block.subarray(0, Math.min(pageSize + offset, blockLength));
|
||||||
|
page.set(subBlock, -offset);
|
||||||
|
} else {
|
||||||
|
// Any other page which intersects the block
|
||||||
|
subBlock = block.subarray(offset, offset + Math.min(pageSize, blockLength - offset));
|
||||||
|
page.set(subBlock, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return outPages;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locates the <tt>Uint8Array</tt> which contains the given offset,
|
||||||
|
* and returns the four bytes held at that offset, as a 32-bit unsigned integer.
|
||||||
|
*
|
||||||
|
*<br/>
|
||||||
|
* Behaviour is similar to {@linkcode https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint32|DataView.prototype.getUint32},
|
||||||
|
* except that this operates over a {@linkcode MemoryMap} instead of
|
||||||
|
* over an <tt>ArrayBuffer</tt>, and that this may return <tt>undefined</tt> if
|
||||||
|
* the address is not <em>entirely</em> contained within one of the <tt>Uint8Array</tt>s.
|
||||||
|
*<br/>
|
||||||
|
*
|
||||||
|
* @param {Number} offset The memory offset to read the data
|
||||||
|
* @param {Boolean} [littleEndian=false] Whether to fetch the 4 bytes as a little- or big-endian integer
|
||||||
|
* @return {Number|undefined} An unsigned 32-bit integer number
|
||||||
|
*/
|
||||||
|
MemoryMap.prototype.getUint32 = function getUint32 (offset, littleEndian) {
|
||||||
|
var this$1 = this;
|
||||||
|
|
||||||
|
var keys = Array.from(this.keys());
|
||||||
|
|
||||||
|
for (var i=0,l=keys.length; i<l; i++) {
|
||||||
|
var blockAddr = keys[i];
|
||||||
|
var block = this$1.get(blockAddr);
|
||||||
|
var blockLength = block.length;
|
||||||
|
var blockEnd = blockAddr + blockLength;
|
||||||
|
|
||||||
|
if (blockAddr <= offset && (offset+4) <= blockEnd) {
|
||||||
|
return (new DataView(block.buffer, offset - blockAddr, 4)).getUint32(0, littleEndian);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a <tt>String</tt> of text representing a .hex file.
|
||||||
|
* <br/>
|
||||||
|
* The writer has an opinionated behaviour. Check the project's
|
||||||
|
* {@link https://github.com/NordicSemiconductor/nrf-intel-hex#Features|README file} for details.
|
||||||
|
*
|
||||||
|
* @param {Number} [lineSize=16] Maximum number of bytes to be encoded in each data record.
|
||||||
|
* Must have a value between 1 and 255, as per the specification.
|
||||||
|
*
|
||||||
|
* @return {String} String of text with the .hex representation of the input binary data
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* import MemoryMap from 'nrf-intel-hex';
|
||||||
|
*
|
||||||
|
* let memMap = new MemoryMap();
|
||||||
|
* let bytes = new Uint8Array(....);
|
||||||
|
* memMap.set(0x0FF80000, bytes); // The block with 'bytes' will start at offset 0x0FF80000
|
||||||
|
*
|
||||||
|
* let string = memMap.asHexString();
|
||||||
|
*/
|
||||||
|
MemoryMap.prototype.asHexString = function asHexString (lineSize) {
|
||||||
|
var this$1 = this;
|
||||||
|
if ( lineSize === void 0 ) lineSize = 16;
|
||||||
|
|
||||||
|
var lowAddress = 0;// 16 least significant bits of the current addr
|
||||||
|
var highAddress = -1 << 16; // 16 most significant bits of the current addr
|
||||||
|
var records = [];
|
||||||
|
if (lineSize <=0) {
|
||||||
|
throw new Error('Size of record must be greater than zero');
|
||||||
|
} else if (lineSize > 255) {
|
||||||
|
throw new Error('Size of record must be less than 256');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Placeholders
|
||||||
|
var offsetRecord = new Uint8Array(6);
|
||||||
|
var recordHeader = new Uint8Array(4);
|
||||||
|
|
||||||
|
var sortedKeys = Array.from(this.keys()).sort(function (a,b){ return a-b; });
|
||||||
|
for (var i=0,l=sortedKeys.length; i<l; i++) {
|
||||||
|
var blockAddr = sortedKeys[i];
|
||||||
|
var block = this$1.get(blockAddr);
|
||||||
|
|
||||||
|
// Sanity checks
|
||||||
|
if (!(block instanceof Uint8Array)) {
|
||||||
|
throw new Error('Block at offset ' + blockAddr + ' is not an Uint8Array');
|
||||||
|
}
|
||||||
|
if (blockAddr < 0) {
|
||||||
|
throw new Error('Block at offset ' + blockAddr + ' has a negative thus invalid address');
|
||||||
|
}
|
||||||
|
var blockSize = block.length;
|
||||||
|
if (!blockSize) { continue; } // Skip zero-length blocks
|
||||||
|
|
||||||
|
|
||||||
|
if (blockAddr > (highAddress + 0xFFFF)) {
|
||||||
|
// Insert a new 0x04 record to jump to a new 64KiB block
|
||||||
|
|
||||||
|
// Round up the least significant 16 bits - no bitmasks because they trigger
|
||||||
|
// base-2 negative numbers, whereas subtracting the modulo maintains precision
|
||||||
|
highAddress = blockAddr - blockAddr % 0x10000;
|
||||||
|
lowAddress = 0;
|
||||||
|
|
||||||
|
offsetRecord[0] = 2;// Length
|
||||||
|
offsetRecord[1] = 0;// Load offset, high byte
|
||||||
|
offsetRecord[2] = 0;// Load offset, low byte
|
||||||
|
offsetRecord[3] = 4;// Record type
|
||||||
|
offsetRecord[4] = highAddress >> 24;// new address offset, high byte
|
||||||
|
offsetRecord[5] = highAddress >> 16;// new address offset, low byte
|
||||||
|
|
||||||
|
records.push(
|
||||||
|
':' +
|
||||||
|
Array.prototype.map.call(offsetRecord, hexpad).join('') +
|
||||||
|
hexpad(checksum(offsetRecord))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blockAddr < (highAddress + lowAddress)) {
|
||||||
|
throw new Error(
|
||||||
|
'Block starting at 0x' +
|
||||||
|
blockAddr.toString(16) +
|
||||||
|
' overlaps with a previous block.');
|
||||||
|
}
|
||||||
|
|
||||||
|
lowAddress = blockAddr % 0x10000;
|
||||||
|
var blockOffset = 0;
|
||||||
|
var blockEnd = blockAddr + blockSize;
|
||||||
|
if (blockEnd > 0xFFFFFFFF) {
|
||||||
|
throw new Error('Data cannot be over 0xFFFFFFFF');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop for every 64KiB memory segment that spans this block
|
||||||
|
while (highAddress + lowAddress < blockEnd) {
|
||||||
|
|
||||||
|
if (lowAddress > 0xFFFF) {
|
||||||
|
// Insert a new 0x04 record to jump to a new 64KiB block
|
||||||
|
highAddress += 1 << 16; // Increase by one
|
||||||
|
lowAddress = 0;
|
||||||
|
|
||||||
|
offsetRecord[0] = 2;// Length
|
||||||
|
offsetRecord[1] = 0;// Load offset, high byte
|
||||||
|
offsetRecord[2] = 0;// Load offset, low byte
|
||||||
|
offsetRecord[3] = 4;// Record type
|
||||||
|
offsetRecord[4] = highAddress >> 24;// new address offset, high byte
|
||||||
|
offsetRecord[5] = highAddress >> 16;// new address offset, low byte
|
||||||
|
|
||||||
|
records.push(
|
||||||
|
':' +
|
||||||
|
Array.prototype.map.call(offsetRecord, hexpad).join('') +
|
||||||
|
hexpad(checksum(offsetRecord))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var recordSize = -1;
|
||||||
|
// Loop for every record for that spans the current 64KiB memory segment
|
||||||
|
while (lowAddress < 0x10000 && recordSize) {
|
||||||
|
recordSize = Math.min(
|
||||||
|
lineSize, // Normal case
|
||||||
|
blockEnd - highAddress - lowAddress, // End of block
|
||||||
|
0x10000 - lowAddress // End of low addresses
|
||||||
|
);
|
||||||
|
|
||||||
|
if (recordSize) {
|
||||||
|
|
||||||
|
recordHeader[0] = recordSize; // Length
|
||||||
|
recordHeader[1] = lowAddress >> 8;// Load offset, high byte
|
||||||
|
recordHeader[2] = lowAddress;// Load offset, low byte
|
||||||
|
recordHeader[3] = 0;// Record type
|
||||||
|
|
||||||
|
var subBlock = block.subarray(blockOffset, blockOffset + recordSize); // Data bytes for this record
|
||||||
|
|
||||||
|
records.push(
|
||||||
|
':' +
|
||||||
|
Array.prototype.map.call(recordHeader, hexpad).join('') +
|
||||||
|
Array.prototype.map.call(subBlock, hexpad).join('') +
|
||||||
|
hexpad(checksumTwo(recordHeader, subBlock))
|
||||||
|
);
|
||||||
|
|
||||||
|
blockOffset += recordSize;
|
||||||
|
lowAddress += recordSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
records.push(':00000001FF');// EOF record
|
||||||
|
|
||||||
|
return records.join('\n');
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a deep copy of the current {@linkcode MemoryMap}, returning a new one
|
||||||
|
* with exactly the same contents, but allocating new memory for each of its
|
||||||
|
* <tt>Uint8Array</tt>s.
|
||||||
|
*
|
||||||
|
* @return {MemoryMap}
|
||||||
|
*/
|
||||||
|
MemoryMap.prototype.clone = function clone () {
|
||||||
|
var this$1 = this;
|
||||||
|
|
||||||
|
var cloned = new MemoryMap();
|
||||||
|
|
||||||
|
for (var [addr, value] of this$1) {
|
||||||
|
cloned.set(addr, new Uint8Array(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
return cloned;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given one <tt>Uint8Array</tt>, looks through its contents and returns a new
|
||||||
|
* {@linkcode MemoryMap}, stripping away those regions where there are only
|
||||||
|
* padding bytes.
|
||||||
|
* <br/>
|
||||||
|
* The start of the input <tt>Uint8Array</tt> is assumed to be offset zero for the output.
|
||||||
|
* <br/>
|
||||||
|
* The use case here is dumping memory from a working device and try to see the
|
||||||
|
* "interesting" memory regions it has. This assumes that there is a constant,
|
||||||
|
* predefined padding byte value being used in the "non-interesting" regions.
|
||||||
|
* In other words: this will work as long as the dump comes from a flash memory
|
||||||
|
* which has been previously erased (thus <tt>0xFF</tt>s for padding), or from a
|
||||||
|
* previously blanked HDD (thus <tt>0x00</tt>s for padding).
|
||||||
|
* <br/>
|
||||||
|
* This method uses <tt>subarray</tt> on the input data, and thus does not allocate memory
|
||||||
|
* for the <tt>Uint8Array</tt>s.
|
||||||
|
*
|
||||||
|
* @param {Uint8Array} bytes The input data
|
||||||
|
* @param {Number} [padByte=0xFF] The value of the byte assumed to be used as padding
|
||||||
|
* @param {Number} [minPadLength=64] The minimum number of consecutive pad bytes to
|
||||||
|
* be considered actual padding
|
||||||
|
*
|
||||||
|
* @return {MemoryMap}
|
||||||
|
*/
|
||||||
|
MemoryMap.fromPaddedUint8Array = function fromPaddedUint8Array (bytes, padByte, minPadLength) {
|
||||||
|
if ( padByte === void 0 ) padByte=0xFF;
|
||||||
|
if ( minPadLength === void 0 ) minPadLength=64;
|
||||||
|
|
||||||
|
|
||||||
|
if (!(bytes instanceof Uint8Array)) {
|
||||||
|
throw new Error('Bytes passed to fromPaddedUint8Array are not an Uint8Array');
|
||||||
|
}
|
||||||
|
|
||||||
|
// The algorithm used is naïve and checks every byte.
|
||||||
|
// An obvious optimization would be to implement Boyer-Moore
|
||||||
|
// (see https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm )
|
||||||
|
// or otherwise start skipping up to minPadLength bytes when going through a non-pad
|
||||||
|
// byte.
|
||||||
|
// Anyway, we could expect a lot of cases where there is a majority of pad bytes,
|
||||||
|
// and the algorithm should check most of them anyway, so the perf gain is questionable.
|
||||||
|
|
||||||
|
var memMap = new MemoryMap();
|
||||||
|
var consecutivePads = 0;
|
||||||
|
var lastNonPad = -1;
|
||||||
|
var firstNonPad = 0;
|
||||||
|
var skippingBytes = false;
|
||||||
|
var l = bytes.length;
|
||||||
|
|
||||||
|
for (var addr = 0; addr < l; addr++) {
|
||||||
|
var byte = bytes[addr];
|
||||||
|
|
||||||
|
if (byte === padByte) {
|
||||||
|
consecutivePads++;
|
||||||
|
if (consecutivePads >= minPadLength) {
|
||||||
|
// Edge case: ignore writing a zero-length block when skipping
|
||||||
|
// bytes at the beginning of the input
|
||||||
|
if (lastNonPad !== -1) {
|
||||||
|
/// Add the previous block to the result memMap
|
||||||
|
memMap.set(firstNonPad, bytes.subarray(firstNonPad, lastNonPad+1));
|
||||||
|
}
|
||||||
|
|
||||||
|
skippingBytes = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (skippingBytes) {
|
||||||
|
skippingBytes = false;
|
||||||
|
firstNonPad = addr;
|
||||||
|
}
|
||||||
|
lastNonPad = addr;
|
||||||
|
consecutivePads = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// At EOF, add the last block if not skipping bytes already (and input not empty)
|
||||||
|
if (!skippingBytes && lastNonPad !== -1) {
|
||||||
|
memMap.set(firstNonPad, bytes.subarray(firstNonPad, l));
|
||||||
|
}
|
||||||
|
|
||||||
|
return memMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new instance of {@linkcode MemoryMap}, containing only data between
|
||||||
|
* the addresses <tt>address</tt> and <tt>address + length</tt>.
|
||||||
|
* Behaviour is similar to {@linkcode https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/slice|Array.prototype.slice},
|
||||||
|
* in that the return value is a portion of the current {@linkcode MemoryMap}.
|
||||||
|
*
|
||||||
|
* <br/>
|
||||||
|
* The returned {@linkcode MemoryMap} might be empty.
|
||||||
|
*
|
||||||
|
* <br/>
|
||||||
|
* Internally, this uses <tt>subarray</tt>, so new memory is not allocated.
|
||||||
|
*
|
||||||
|
* @param {Number} address The start address of the slice
|
||||||
|
* @param {Number} length The length of memory map to slice out
|
||||||
|
* @return {MemoryMap}
|
||||||
|
*/
|
||||||
|
MemoryMap.prototype.slice = function slice (address, length){
|
||||||
|
var this$1 = this;
|
||||||
|
if ( length === void 0 ) length = Infinity;
|
||||||
|
|
||||||
|
if (length < 0) {
|
||||||
|
throw new Error('Length of the slice cannot be negative');
|
||||||
|
}
|
||||||
|
|
||||||
|
var sliced = new MemoryMap();
|
||||||
|
|
||||||
|
for (var [blockAddr, block] of this$1) {
|
||||||
|
var blockLength = block.length;
|
||||||
|
|
||||||
|
if ((blockAddr + blockLength) >= address && blockAddr < (address + length)) {
|
||||||
|
var sliceStart = Math.max(address, blockAddr);
|
||||||
|
var sliceEnd = Math.min(address + length, blockAddr + blockLength);
|
||||||
|
var sliceLength = sliceEnd - sliceStart;
|
||||||
|
var relativeSliceStart = sliceStart - blockAddr;
|
||||||
|
|
||||||
|
if (sliceLength > 0) {
|
||||||
|
sliced.set(sliceStart, block.subarray(relativeSliceStart, relativeSliceStart + sliceLength));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sliced;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new instance of {@linkcode https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint32|Uint8Array}, containing only data between
|
||||||
|
* the addresses <tt>address</tt> and <tt>address + length</tt>. Any byte without a value
|
||||||
|
* in the input {@linkcode MemoryMap} will have a value of <tt>padByte</tt>.
|
||||||
|
*
|
||||||
|
* <br/>
|
||||||
|
* This method allocates new memory.
|
||||||
|
*
|
||||||
|
* @param {Number} address The start address of the slice
|
||||||
|
* @param {Number} length The length of memory map to slice out
|
||||||
|
* @param {Number} [padByte=0xFF] The value of the byte assumed to be used as padding
|
||||||
|
* @return {MemoryMap}
|
||||||
|
*/
|
||||||
|
MemoryMap.prototype.slicePad = function slicePad (address, length, padByte){
|
||||||
|
var this$1 = this;
|
||||||
|
if ( padByte === void 0 ) padByte=0xFF;
|
||||||
|
|
||||||
|
if (length < 0) {
|
||||||
|
throw new Error('Length of the slice cannot be negative');
|
||||||
|
}
|
||||||
|
|
||||||
|
var out = (new Uint8Array(length)).fill(padByte);
|
||||||
|
|
||||||
|
for (var [blockAddr, block] of this$1) {
|
||||||
|
var blockLength = block.length;
|
||||||
|
|
||||||
|
if ((blockAddr + blockLength) >= address && blockAddr < (address + length)) {
|
||||||
|
var sliceStart = Math.max(address, blockAddr);
|
||||||
|
var sliceEnd = Math.min(address + length, blockAddr + blockLength);
|
||||||
|
var sliceLength = sliceEnd - sliceStart;
|
||||||
|
var relativeSliceStart = sliceStart - blockAddr;
|
||||||
|
|
||||||
|
if (sliceLength > 0) {
|
||||||
|
out.set(block.subarray(relativeSliceStart, relativeSliceStart + sliceLength), sliceStart - address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the current memory map contains the one given as a parameter.
|
||||||
|
*
|
||||||
|
* <br/>
|
||||||
|
* "Contains" means that all the offsets that have a byte value in the given
|
||||||
|
* memory map have a value in the current memory map, and that the byte values
|
||||||
|
* are the same.
|
||||||
|
*
|
||||||
|
* <br/>
|
||||||
|
* An empty memory map is always contained in any other memory map.
|
||||||
|
*
|
||||||
|
* <br/>
|
||||||
|
* Returns boolean <tt>true</tt> if the memory map is contained, <tt>false</tt>
|
||||||
|
* otherwise.
|
||||||
|
*
|
||||||
|
* @param {MemoryMap} memMap The memory map to check
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
MemoryMap.prototype.contains = function contains (memMap) {
|
||||||
|
var this$1 = this;
|
||||||
|
|
||||||
|
for (var [blockAddr, block] of memMap) {
|
||||||
|
|
||||||
|
var blockLength = block.length;
|
||||||
|
|
||||||
|
var slice = this$1.slice(blockAddr, blockLength).join().get(blockAddr);
|
||||||
|
|
||||||
|
if ((!slice) || slice.length !== blockLength ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i in block) {
|
||||||
|
if (block[i] !== slice[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.defineProperties( MemoryMap.prototype, prototypeAccessors );
|
||||||
|
|
||||||
|
return MemoryMap;
|
||||||
|
|
||||||
|
})));
|
||||||
|
//# sourceMappingURL=intel-hex.browser.js.map
|
158
web/js/wallet.js
158
web/js/wallet.js
@ -76,6 +76,9 @@ function array2websafe(array) {
|
|||||||
function string2websafe(string) {
|
function string2websafe(string) {
|
||||||
return webSafe64(window.btoa(string));
|
return webSafe64(window.btoa(string));
|
||||||
}
|
}
|
||||||
|
function websafe2string(string) {
|
||||||
|
return window.atob(normal64(string));
|
||||||
|
}
|
||||||
function string2array(string) {
|
function string2array(string) {
|
||||||
|
|
||||||
var bytes = new Uint8Array( string.length );
|
var bytes = new Uint8Array( string.length );
|
||||||
@ -233,6 +236,10 @@ var CMD = {
|
|||||||
reset: 0x13,
|
reset: 0x13,
|
||||||
version: 0x14,
|
version: 0x14,
|
||||||
rng: 0x15,
|
rng: 0x15,
|
||||||
|
boot_write: 0x40,
|
||||||
|
boot_done: 0x41,
|
||||||
|
boot_check: 0x42,
|
||||||
|
boot_erase: 0x43,
|
||||||
};
|
};
|
||||||
|
|
||||||
var PIN = {
|
var PIN = {
|
||||||
@ -305,6 +312,31 @@ function send_msg_http(data, func, timeout) {
|
|||||||
xhr.send(req);
|
xhr.send(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function get_firmware_http_(func) {
|
||||||
|
var url = 'https://localhost:8080';
|
||||||
|
|
||||||
|
var xhr = createCORSRequest('GET', url);
|
||||||
|
|
||||||
|
if (!xhr) {
|
||||||
|
console.log('CORS not supported');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response handlers.
|
||||||
|
xhr.onload = function() {
|
||||||
|
var text = xhr.responseText;
|
||||||
|
var resp = JSON.parse(text);
|
||||||
|
resp.firmware = websafe2string(resp.firmware);
|
||||||
|
if (func) func(resp);
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.onerror = function() {
|
||||||
|
console.log('Woops, there was an error making the request.');
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.send();
|
||||||
|
}
|
||||||
|
|
||||||
// For real
|
// For real
|
||||||
function send_msg_u2f(data, func, timeout) {
|
function send_msg_u2f(data, func, timeout) {
|
||||||
// Use key handle and signature response as comm channel
|
// Use key handle and signature response as comm channel
|
||||||
@ -337,6 +369,8 @@ function send_msg_u2f(data, func, timeout) {
|
|||||||
|
|
||||||
var d2 = new Date();
|
var d2 = new Date();
|
||||||
t2 = d2.getTime();
|
t2 = d2.getTime();
|
||||||
|
if (!res.signatureData)
|
||||||
|
func(res);
|
||||||
sig = websafe2array(res.signatureData)
|
sig = websafe2array(res.signatureData)
|
||||||
data = parse_device_response(sig);
|
data = parse_device_response(sig);
|
||||||
func(data);
|
func(data);
|
||||||
@ -351,6 +385,37 @@ if (DEVELOPMENT) {
|
|||||||
send_msg = send_msg_u2f;
|
send_msg = send_msg_u2f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatBootRequest(cmd, addr, data) {
|
||||||
|
var array = new Uint8Array(255);
|
||||||
|
|
||||||
|
addr = addr || 0x8000;
|
||||||
|
|
||||||
|
data = data || new Uint8Array(1);
|
||||||
|
|
||||||
|
if (data.length > (255 - 9)) {
|
||||||
|
throw new Error("Max size exceeded");
|
||||||
|
}
|
||||||
|
|
||||||
|
array[0] = cmd & 0xff;
|
||||||
|
array[1] = (addr >> 0) & 0xff;
|
||||||
|
array[2] = (addr >> 8) & 0xff;
|
||||||
|
array[3] = (addr >> 16) & 0xff;
|
||||||
|
|
||||||
|
array[4] = 0x8C; // Wallet tag. To not interfere with U2F devices.
|
||||||
|
array[5] = 0x27;
|
||||||
|
array[6] = 0x90;
|
||||||
|
array[7] = 0xf6;
|
||||||
|
|
||||||
|
array[8] = data.length & 0xff;
|
||||||
|
|
||||||
|
var offset = 9;
|
||||||
|
|
||||||
|
var i;
|
||||||
|
for (i = 0; i < data.length; i++){
|
||||||
|
array[offset + i] = data[i];
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
// Format a request message
|
// Format a request message
|
||||||
// @cmd 0-255 value command
|
// @cmd 0-255 value command
|
||||||
@ -804,6 +869,40 @@ var get_rng_ = function(func){
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var is_bootloader_ = function(func){
|
||||||
|
|
||||||
|
var req = formatBootRequest(CMD.boot_check);
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
send_msg(req, function(resp){
|
||||||
|
if (func)func(resp);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var bootloader_finish_ = function(func){
|
||||||
|
|
||||||
|
var req = formatBootRequest(CMD.boot_done);
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
send_msg(req, function(resp){
|
||||||
|
if (func)func(resp);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var bootloader_write_ = function(addr,data,func){
|
||||||
|
|
||||||
|
var req = formatBootRequest(CMD.boot_write,addr,data);
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
send_msg(req, function(resp){
|
||||||
|
if (func)func(resp);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
function wrap_promise(func)
|
function wrap_promise(func)
|
||||||
{
|
{
|
||||||
var self = this;
|
var self = this;
|
||||||
@ -836,6 +935,8 @@ function wrap_promise(func)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var get_firmware_http = wrap_promise(get_firmware_http_);
|
||||||
|
|
||||||
function WalletDevice() {
|
function WalletDevice() {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.shared_secret = null;
|
this.shared_secret = null;
|
||||||
@ -885,6 +986,12 @@ function WalletDevice() {
|
|||||||
|
|
||||||
this.get_rng = get_rng_;
|
this.get_rng = get_rng_;
|
||||||
|
|
||||||
|
this.is_bootloader = is_bootloader_;
|
||||||
|
|
||||||
|
this.bootloader_write = bootloader_write_;
|
||||||
|
|
||||||
|
this.bootloader_finish = bootloader_finish_;
|
||||||
|
|
||||||
this.init = wrap_promise.call(this, this.init);
|
this.init = wrap_promise.call(this, this.init);
|
||||||
this.get_version = wrap_promise.call(this, this.get_version);
|
this.get_version = wrap_promise.call(this, this.get_version);
|
||||||
this.get_shared_secret = wrap_promise.call(this, this.get_shared_secret );
|
this.get_shared_secret = wrap_promise.call(this, this.get_shared_secret );
|
||||||
@ -897,6 +1004,9 @@ function WalletDevice() {
|
|||||||
this.register = wrap_promise.call(this, this.register );
|
this.register = wrap_promise.call(this, this.register );
|
||||||
this.reset = wrap_promise.call(this,this.reset );
|
this.reset = wrap_promise.call(this,this.reset );
|
||||||
this.get_rng = wrap_promise.call(this,this.get_rng);
|
this.get_rng = wrap_promise.call(this,this.get_rng);
|
||||||
|
this.is_bootloader = wrap_promise.call(this,this.is_bootloader);
|
||||||
|
this.bootloader_write = wrap_promise.call(this,this.bootloader_write);
|
||||||
|
this.bootloader_finish = wrap_promise.call(this,this.bootloader_finish);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1205,19 +1315,55 @@ async function run_tests() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while(1)
|
async function test_bootloader()
|
||||||
{
|
{
|
||||||
await device_start_over();
|
var addr = 0x8000;
|
||||||
await test_pin();
|
|
||||||
await test_crypto();
|
var p = await dev.is_bootloader();
|
||||||
await test_rng();
|
TEST(p.status == 'CTAP1_SUCCESS', 'Device is in bootloader mode');
|
||||||
|
|
||||||
|
p = await get_firmware_http();
|
||||||
|
|
||||||
|
var blocks = MemoryMap.fromHex(p.firmware);
|
||||||
|
var addresses = blocks.keys();
|
||||||
|
|
||||||
|
var addr = addresses.next();
|
||||||
|
var chunk_size = 244;
|
||||||
|
while(!addr.done) {
|
||||||
|
var data = blocks.get(addr.value);
|
||||||
|
var i;
|
||||||
|
for (i = 0; i < data.length; i += chunk_size) {
|
||||||
|
var chunk = data.slice(i,i+chunk_size);
|
||||||
|
p = await dev.bootloader_write(addr.value + i, chunk);
|
||||||
|
TEST(p.status == 'CTAP1_SUCCESS', 'Device wrote data');
|
||||||
|
var progress = (((i/data.length) * 100 * 100) | 0)/100;
|
||||||
|
document.getElementById('progress').textContent = ''+progress+' %';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addr = addresses.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
p = await dev.bootloader_finish();
|
||||||
|
console.log(p);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//while(1)
|
||||||
|
//{
|
||||||
|
//await device_start_over();
|
||||||
|
//await test_pin();
|
||||||
|
//await test_crypto();
|
||||||
|
//await test_rng();
|
||||||
|
//}
|
||||||
//await benchmark();
|
//await benchmark();
|
||||||
//await test_persistence();
|
//await test_persistence();
|
||||||
|
|
||||||
}
|
await test_bootloader();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
var test;
|
||||||
|
|
||||||
EC = elliptic.ec
|
EC = elliptic.ec
|
||||||
|
|
||||||
run_tests()
|
run_tests()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user