Compare commits

..

3 Commits

20 changed files with 197 additions and 1100 deletions

View File

@ -61,11 +61,9 @@ git checkout ${VERSION_TO_BUILD}
git submodule update --init --recursive
```
## Installing the toolchain and applying updates
## Installing the toolchain
In order to compile ARM code, you need the ARM compiler and other things like bundling bootloader and firmware require the [solo-python](https://github.com/solokeys/solo-python) python package. Check our [documentation](https://docs.solokeys.io/solo/) for details.
You can update your solokey after running `pip3 install solo-python` with `solo key update` for the latest version. To apply a custom image use `solo program bootloader <file>(.json|.hex)`.
In order to compile ARM code, you need the ARM compiler and other things like bundling bootloader and firmware require the `solo-python` python package. Check our [documentation](https://docs.solokeys.io/solo/) for details
## Installing the toolkit and compiling in Docker
Alternatively, you can use Docker to create a container with the toolchain.

View File

@ -1 +1 @@
4.0.0
3.1.0

View File

@ -104,24 +104,9 @@ solo mergehex bootloader.hex solo.hex bundle.hex
`bundle.hex` is our complete firmware build. Note it is in this step that you can
include a custom attestation certificate or lock the device from debugging/DFU.
By default the "hacker" attestation certifcate and key is used. Use the `--lock` flag
to make this permanent.
By default the "hacker" attestation certifcate and key is used.
```
solo mergehex \
--attestation-key "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" \
--attestation-cert attestation.der \
solo.hex \
bootloader.hex \
bundle.hex
```
**Warning**: If you use `--lock`, this will permanently lock the device to this new bootloader. You
won't be able to program the bootloader again or be able to connect a hardware debugger.
The new bootloader may be able to accept (signed) updates still, depending on how you configured it.
```
# Permanent!
solo mergehex \
--attestation-key "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" \
--attestation-cert attestation.der \
@ -133,5 +118,9 @@ solo mergehex \
See [here for more information on custom attestation](/solo/customization/).
If you use `--lock`, this will permanently lock the device to this new bootloader. You
won't be able to program the bootloader again or be able to connect a hardware debugger.
The new bootloader may be able to accept (signed) updates still, depending on how you configured it.
To learn more about normal updates or a "full" update, you should [read more on Solo's boot stages](/solo/bootloader-mode).

View File

@ -74,7 +74,7 @@ Note you must use a prime256v1 curve for this step, and you must leave the unit/
country=US
state=Maine
organization=OpenSourceSecurity
unit="Authenticator Attestation" # MUST KEEP THIS AS "Authenticator Attestation" for FIDO2.
unit="Authenticator Attestation"
CN=example.com
email=example@example.com
@ -134,8 +134,6 @@ solo mergehex \
bundle.hex
```
**Warning**: Using the `--lock` flag prevents the DFU from being accessed on the device again. It's recommended to try first without the `--lock` flag to make sure it works.
Now you have a newly created `bundle.hex` file with a custom attestation key and cert. You can [program this `bundle.hex` file
with Solo in DFU mode](/solo/programming#procedure).

File diff suppressed because it is too large Load Diff

View File

@ -16,9 +16,7 @@
#define CTAP_CLIENT_PIN 0x06
#define CTAP_RESET 0x07
#define GET_NEXT_ASSERTION 0x08
#define CTAP_CBOR_CRED_MGMT 0x0A
#define CTAP_VENDOR_FIRST 0x40
#define CTAP_CBOR_CRED_MGMT_PRE 0x41
#define CTAP_VENDOR_LAST 0xBF
#define MC_clientDataHash 0x01
@ -39,19 +37,6 @@
#define GA_pinAuth 0x06
#define GA_pinProtocol 0x07
#define CM_cmd 0x01
#define CM_cmdMetadata 0x01
#define CM_cmdRPBegin 0x02
#define CM_cmdRPNext 0x03
#define CM_cmdRKBegin 0x04
#define CM_cmdRKNext 0x05
#define CM_cmdRKDelete 0x06
#define CM_subCommandParams 0x02
#define CM_subCommandRpId 0x01
#define CM_subCommandCred 0x02
#define CM_pinProtocol 0x03
#define CM_pinAuth 0x04
#define CP_pinProtocol 0x01
#define CP_subCommand 0x02
#define CP_cmdGetRetries 0x01
@ -73,11 +58,6 @@
#define EXT_HMAC_SECRET_REQUESTED 0x01
#define EXT_HMAC_SECRET_PARSED 0x02
#define EXT_CRED_PROTECT_INVALID 0x00
#define EXT_CRED_PROTECT_OPTIONAL 0x01
#define EXT_CRED_PROTECT_OPTIONAL_WITH_CREDID 0x02
#define EXT_CRED_PROTECT_REQUIRED 0x03
#define RESP_versions 0x1
#define RESP_extensions 0x2
#define RESP_aaguid 0x3
@ -161,29 +141,16 @@ typedef struct
typedef struct {
uint8_t tag[CREDENTIAL_TAG_SIZE];
union {
uint8_t nonce[CREDENTIAL_NONCE_SIZE];
struct {
uint8_t _pad[CREDENTIAL_NONCE_SIZE - 4];
uint32_t value;
}__attribute__((packed)) metadata;
}__attribute__((packed)) entropy;
uint8_t rpIdHash[32];
uint32_t count;
}__attribute__((packed)) CredentialId;
struct __attribute__((packed)) Credential {
struct Credential {
CredentialId id;
CTAP_userEntity user;
};
typedef struct {
CredentialId id;
CTAP_userEntity user;
// Maximum amount of "extra" space in resident key.
uint8_t rpId[48];
uint8_t rpIdSize;
} __attribute__((packed)) CTAP_residentKey;
typedef struct Credential CTAP_residentKey;
typedef struct
{
@ -250,7 +217,6 @@ typedef struct
{
uint8_t hmac_secret_present;
CTAP_hmac_secret hmac_secret;
uint32_t cred_protect;
} CTAP_extensions;
typedef struct
@ -319,26 +285,6 @@ typedef struct
} CTAP_getAssertion;
typedef struct
{
int cmd;
struct {
uint8_t rpIdHash[32];
CTAP_credentialDescriptor credentialDescriptor;
} subCommandParams;
struct {
uint8_t cmd;
uint8_t subCommandParamsCborCopy[sizeof(CTAP_credentialDescriptor) + 16];
} hashed;
uint32_t subCommandParamsCborSize;
uint8_t pinAuth[16];
uint8_t pinAuthPresent;
int pinProtocol;
} CTAP_credMgmt;
typedef struct
{
int pinProtocol;
@ -357,12 +303,7 @@ typedef struct
struct _getAssertionState {
// Room for both authData struct and extensions
struct {
CTAP_authDataHeader authData;
uint8_t extensions[80];
} __attribute__((packed)) buf;
CTAP_extensions extensions;
uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE];
CTAP_credentialDescriptor creds[ALLOW_LIST_MAX_SIZE];
uint8_t lastcmd;

View File

@ -666,8 +666,8 @@ uint8_t ctap_parse_extensions(CborValue * val, CTAP_extensions * ext)
if (ret == CborErrorOutOfMemory)
{
printf2(TAG_ERR,"Error, rp map key is too large. Ignoring.\n");
check_ret( cbor_value_advance(&map) );
check_ret( cbor_value_advance(&map) );
cbor_value_advance(&map);
cbor_value_advance(&map);
continue;
}
check_ret(ret);
@ -698,14 +698,6 @@ uint8_t ctap_parse_extensions(CborValue * val, CTAP_extensions * ext)
printf1(TAG_RED, "warning: hmac_secret request ignored for being wrong type\r\n");
}
}
else if (strncmp(key, "credProtect",11) == 0) {
if (cbor_value_get_type(&map) == CborIntegerType) {
ret = cbor_value_get_int(&map, (int*)&ext->cred_protect);
check_ret(ret);
} else {
printf1(TAG_RED, "warning: credProtect request ignored for being wrong type\r\n");
}
}
ret = cbor_value_advance(&map);
check_ret(ret);
@ -879,7 +871,7 @@ uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encod
{
return ret;
}
ret = cbor_value_advance(&map);
cbor_value_advance(&map);
check_ret(ret);
}
@ -1007,163 +999,6 @@ uint8_t parse_allow_list(CTAP_getAssertion * GA, CborValue * it)
return 0;
}
static uint8_t parse_cred_mgmt_subcommandparams(CborValue * val, CTAP_credMgmt * CM)
{
size_t map_length;
int key;
int ret;
unsigned int i;
CborValue map;
size_t sz = 32;
if (cbor_value_get_type(val) != CborMapType)
{
printf2(TAG_ERR,"error, wrong type\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
ret = cbor_value_enter_container(val,&map);
check_ret(ret);
const uint8_t * start_byte = cbor_value_get_next_byte(&map) - 1;
ret = cbor_value_get_map_length(val, &map_length);
check_ret(ret);
for (i = 0; i < map_length; i++)
{
if (cbor_value_get_type(&map) != CborIntegerType)
{
printf2(TAG_ERR,"Error, expecting integer type for map key, got %s\n", cbor_value_get_type_string(&map));
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
ret = cbor_value_get_int(&map, &key);
check_ret(ret);
ret = cbor_value_advance(&map);
check_ret(ret);
switch(key)
{
case CM_subCommandRpId:
ret = cbor_value_copy_byte_string(&map, CM->subCommandParams.rpIdHash, &sz, NULL);
if (ret == CborErrorOutOfMemory)
{
printf2(TAG_ERR,"Error, map key is too large\n");
return CTAP2_ERR_LIMIT_EXCEEDED;
}
check_ret(ret);
break;
case CM_subCommandCred:
ret = parse_credential_descriptor(&map, &CM->subCommandParams.credentialDescriptor);
check_ret(ret);;
break;
}
ret = cbor_value_advance(&map);
check_ret(ret);
}
const uint8_t * end_byte = cbor_value_get_next_byte(&map);
uint32_t length = (uint32_t)end_byte - (uint32_t)start_byte;
if (length > sizeof(CM->hashed.subCommandParamsCborCopy))
{
return CTAP2_ERR_LIMIT_EXCEEDED;
}
// Copy the details that were hashed so they can be verified later.
memmove(CM->hashed.subCommandParamsCborCopy, start_byte, length);
CM->subCommandParamsCborSize = length;
return 0;
}
uint8_t ctap_parse_cred_mgmt(CTAP_credMgmt * CM, uint8_t * request, int length)
{
int ret;
unsigned int i;
int key;
size_t map_length;
CborParser parser;
CborValue it,map;
memset(CM, 0, sizeof(CTAP_credMgmt));
ret = cbor_parser_init(request, length, CborValidateCanonicalFormat, &parser, &it);
check_ret(ret);
CborType type = cbor_value_get_type(&it);
if (type != CborMapType)
{
printf2(TAG_ERR,"Error, expecting cbor map\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
ret = cbor_value_enter_container(&it,&map);
check_ret(ret);
ret = cbor_value_get_map_length(&it, &map_length);
check_ret(ret);
printf1(TAG_PARSE, "CM map has %d elements\n", map_length);
for (i = 0; i < map_length; i++)
{
type = cbor_value_get_type(&map);
if (type != CborIntegerType)
{
printf2(TAG_ERR,"Error, expecting int for map key\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
ret = cbor_value_get_int_checked(&map, &key);
check_ret(ret);
ret = cbor_value_advance(&map);
check_ret(ret);
switch(key)
{
case CM_cmd:
printf1(TAG_PARSE, "CM_cmd\n");
if (cbor_value_get_type(&map) == CborIntegerType)
{
ret = cbor_value_get_int_checked(&map, &CM->cmd);
check_ret(ret);
CM->hashed.cmd = CM->cmd;
}
else
{
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
break;
case CM_subCommandParams:
printf1(TAG_PARSE, "CM_subCommandParams\n");
ret = parse_cred_mgmt_subcommandparams(&map, CM);
check_ret(ret);
break;
case CM_pinProtocol:
printf1(TAG_PARSE, "CM_pinProtocol\n");
if (cbor_value_get_type(&map) == CborIntegerType)
{
ret = cbor_value_get_int_checked(&map, &CM->pinProtocol);
check_ret(ret);
}
else
{
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
break;
case CM_pinAuth:
printf1(TAG_PARSE, "CM_pinAuth\n");
ret = parse_fixed_byte_string(&map, CM->pinAuth, 16);
check_retr(ret);
CM->pinAuthPresent = 1;
break;
}
ret = cbor_value_advance(&map);
check_ret(ret);
}
return 0;
}
uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int length)
{
@ -1297,7 +1132,7 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int
return ret;
}
ret = cbor_value_advance(&map);
cbor_value_advance(&map);
check_ret(ret);
}
@ -1518,21 +1353,11 @@ uint8_t ctap_parse_client_pin(CTAP_clientPin * CP, uint8_t * request, int length
break;
case CP_getKeyAgreement:
printf1(TAG_CP,"CP_getKeyAgreement\n");
if (cbor_value_get_type(&map) != CborBooleanType)
{
printf2(TAG_ERR,"Error, expecting cbor boolean\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
ret = cbor_value_get_boolean(&map, &CP->getKeyAgreement);
check_ret(ret);
break;
case CP_getRetries:
printf1(TAG_CP,"CP_getRetries\n");
if (cbor_value_get_type(&map) != CborBooleanType)
{
printf2(TAG_ERR,"Error, expecting cbor boolean\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
ret = cbor_value_get_boolean(&map, &CP->getRetries);
check_ret(ret);
break;

View File

@ -35,7 +35,6 @@ uint8_t parse_cose_key(CborValue * it, COSE_key * cose);
uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encoder, uint8_t * request, int length);
uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int length);
uint8_t ctap_parse_cred_mgmt(CTAP_credMgmt * CM, uint8_t * request, int length);
uint8_t ctap_parse_client_pin(CTAP_clientPin * CP, uint8_t * request, int length);
uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor * cred);

View File

@ -734,11 +734,6 @@ uint8_t ctaphid_custom_command(int len, CTAP_RESPONSE * ctap_resp, CTAPHID_WRITE
ctaphid_write(wb, NULL, 0);
return 1;
#endif
#if defined(SOLO)
case CTAPHID_REBOOT:
device_reboot();
return 1;
#endif
#if !defined(IS_BOOTLOADER)
case CTAPHID_GETRNG:
@ -784,37 +779,30 @@ uint8_t ctaphid_custom_command(int len, CTAP_RESPONSE * ctap_resp, CTAPHID_WRITE
case CTAPHID_LOADKEY:
/**
* Load external key. Useful for enabling backups.
* bytes: 4 4 96
* payload: version [maj rev patch RFU]| counter_replacement (BE) | master_key |
* bytes: 4 96
* payload: | counter_increase (BE) | master_key |
*
* Counter should be increased by a large amount, e.g. (0x10000000)
* to outdo any previously lost/broken keys.
*/
printf1(TAG_HID,"CTAPHID_LOADKEY\n");
if (len != 104)
if (len != 100)
{
printf2(TAG_ERR,"Error, invalid length.\n");
ctaphid_send_error(wb->cid, CTAP1_ERR_INVALID_LENGTH);
return 1;
}
param = ctap_buffer[0] << 16;
param |= ctap_buffer[1] << 8;
param |= ctap_buffer[2] << 0;
if (param != 0){
ctaphid_send_error(wb->cid, CTAP2_ERR_UNSUPPORTED_OPTION);
return 1;
}
// Ask for THREE button presses
if (ctap_user_presence_test(8000) > 0)
if (ctap_user_presence_test(2000) > 0)
if (ctap_user_presence_test(2000) > 0)
if (ctap_user_presence_test(8000) > 0)
if (ctap_user_presence_test(8000) > 0)
{
ctap_load_external_keys(ctap_buffer + 8);
param = ctap_buffer[7];
param |= ctap_buffer[6] << 8;
param |= ctap_buffer[5] << 16;
param |= ctap_buffer[4] << 24;
ctap_load_external_keys(ctap_buffer + 4);
param = ctap_buffer[3];
param |= ctap_buffer[2] << 8;
param |= ctap_buffer[1] << 16;
param |= ctap_buffer[0] << 24;
ctap_atomic_count(param);
wb->bcnt = 0;

View File

@ -27,7 +27,6 @@
#define CTAPHID_BOOT (TYPE_INIT | 0x50)
#define CTAPHID_ENTERBOOT (TYPE_INIT | 0x51)
#define CTAPHID_ENTERSTBOOT (TYPE_INIT | 0x52)
#define CTAPHID_REBOOT (TYPE_INIT | 0x53)
#define CTAPHID_GETRNG (TYPE_INIT | 0x60)
#define CTAPHID_GETVERSION (TYPE_INIT | 0x61)
#define CTAPHID_LOADKEY (TYPE_INIT | 0x62)

View File

@ -185,22 +185,6 @@ __attribute__((weak)) void ctap_store_rk(int index, CTAP_residentKey * rk)
}
__attribute__((weak)) void ctap_delete_rk(int index)
{
CTAP_residentKey rk;
memset(&rk, 0xff, sizeof(CTAP_residentKey));
if (index < RK_NUM)
{
memmove(RK_STORE.rks + index, &rk, sizeof(CTAP_residentKey));
}
else
{
printf1(TAG_ERR,"Out of bounds for delete_rk\r\n");
}
}
__attribute__((weak)) void ctap_load_rk(int index, CTAP_residentKey * rk)
{
memmove(rk, RK_STORE.rks + index, sizeof(CTAP_residentKey));

View File

@ -140,13 +140,6 @@ uint32_t ctap_rk_size();
*/
void ctap_store_rk(int index,CTAP_residentKey * rk);
/** Delete a resident key from an index.
* @param index to delete resident key from. Has no effect if no RK exists at index.
*
* *Optional*, if not implemented, operates on non-persistant RK's.
*/
void ctap_delete_rk(int index);
/** Read a resident key from an index into memory
* @param index to read resident key from.
* @param rk pointer to resident key structure to write into with RK.

View File

@ -51,7 +51,6 @@ struct logtag tagtable[] = {
{TAG_NFC,"NFC"},
{TAG_NFC_APDU, "NAPDU"},
{TAG_CCID, "CCID"},
{TAG_CM, "CRED_MGMT"},
};

View File

@ -48,7 +48,6 @@ typedef enum
TAG_NFC = (1 << 19),
TAG_NFC_APDU = (1 << 20),
TAG_CCID = (1 << 21),
TAG_CM = (1 << 22),
TAG_NO_TAG = (1UL << 30),
TAG_FILENO = (1UL << 31)

View File

@ -204,7 +204,7 @@ int8_t u2f_authenticate_credential(struct u2f_key_handle * kh, uint8_t key_handl
printf1(TAG_U2F, "APPID does not match rpIdHash.\n");
return 0;
}
make_auth_tag(appid, (uint8_t*)&cred->entropy, cred->count, tag);
make_auth_tag(appid, cred->nonce, cred->count, tag);
if (memcmp(cred->tag, tag, CREDENTIAL_TAG_SIZE) == 0){
return 1;

View File

@ -1,17 +1,12 @@
#include "version.h"
#include "app.h"
const version_t firmware_version
#ifdef SOLO
__attribute__ ((section (".flag"))) __attribute__ ((__used__))
#endif
= {
const version_t firmware_version __attribute__ ((section (".flag"))) __attribute__ ((__used__)) = {
.major = SOLO_VERSION_MAJ,
.minor = SOLO_VERSION_MIN,
.patch = SOLO_VERSION_PATCH,
.reserved = 0
};
};
// from tinycbor, for a quick static_assert
#include <compilersupport_p.h>

View File

@ -449,12 +449,6 @@ void ctap_store_rk(int index, CTAP_residentKey * rk)
}
void ctap_delete_rk(int index)
{
CTAP_residentKey rk;
memset(&rk, 0xff, sizeof(CTAP_residentKey));
memmove(RK_STORE.rks + index, &rk, sizeof(CTAP_residentKey));
}
void ctap_load_rk(int index, CTAP_residentKey * rk)
{

View File

@ -84,5 +84,4 @@ cbor:
cd ../../tinycbor/ && make clean
cd ../../tinycbor/ && make CC="$(CC)" AR=$(AR) \
LDFLAGS="$(LDFLAGS_LIB)" \
CFLAGS="$(CFLAGS) -Os -DCBOR_PARSER_MAX_RECURSIONS=3"
CFLAGS="$(CFLAGS) -Os"

View File

@ -61,7 +61,7 @@ SECTIONS
*(.data*)
. = ALIGN(8);
_edata = .;
} >sram2 AT> flash
} >ram AT> flash
.flag :
{

View File

@ -577,11 +577,7 @@ uint32_t ctap_atomic_count(uint32_t amount)
return lastc;
}
if (amount > 256){
lastc = amount;
} else {
lastc += amount;
}
if (lastc/256 > erases)
{
@ -790,28 +786,33 @@ uint32_t ctap_rk_size(void)
void ctap_store_rk(int index,CTAP_residentKey * rk)
{
ctap_overwrite_rk(index, rk);
}
int page_offset = (sizeof(CTAP_residentKey) * index) / PAGE_SIZE;
uint32_t addr = flash_addr(page_offset + RK_START_PAGE) + ((sizeof(CTAP_residentKey)*index) % PAGE_SIZE);
void ctap_delete_rk(int index)
{
CTAP_residentKey rk;
memset(&rk, 0xff, sizeof(CTAP_residentKey));
ctap_overwrite_rk(index, &rk);
printf1(TAG_GREEN, "storing RK %d @ %04x\r\n", index,addr);
if (page_offset < RK_NUM_PAGES)
{
flash_write(addr, (uint8_t*)rk, sizeof(CTAP_residentKey));
//dump_hex1(TAG_GREEN,rk,sizeof(CTAP_residentKey));
}
else
{
printf2(TAG_ERR,"Out of bounds reading index %d for rk\n", index);
}
}
void ctap_load_rk(int index,CTAP_residentKey * rk)
{
int byte_offset_into_page = (sizeof(CTAP_residentKey) * (index % (PAGE_SIZE/sizeof(CTAP_residentKey))));
int page_offset = (index)/(PAGE_SIZE/sizeof(CTAP_residentKey));
uint32_t addr = flash_addr(page_offset + RK_START_PAGE) + byte_offset_into_page;
int page_offset = (sizeof(CTAP_residentKey) * index) / PAGE_SIZE;
uint32_t addr = flash_addr(page_offset + RK_START_PAGE) + ((sizeof(CTAP_residentKey)*index) % PAGE_SIZE);
printf1(TAG_GREEN, "reading RK %d @ %04x\r\n", index, addr);
if (page_offset < RK_NUM_PAGES)
{
uint32_t * ptr = (uint32_t *)addr;
memmove((uint8_t*)rk,ptr,sizeof(CTAP_residentKey));
//dump_hex1(TAG_GREEN,rk,sizeof(CTAP_residentKey));
}
else
{
@ -822,28 +823,22 @@ void ctap_load_rk(int index,CTAP_residentKey * rk)
void ctap_overwrite_rk(int index,CTAP_residentKey * rk)
{
uint8_t tmppage[PAGE_SIZE];
int page_offset = (sizeof(CTAP_residentKey) * index) / PAGE_SIZE;
int page = page_offset + RK_START_PAGE;
int byte_offset_into_page = (sizeof(CTAP_residentKey) * (index % (PAGE_SIZE/sizeof(CTAP_residentKey))));
int page_offset = (index)/(PAGE_SIZE/sizeof(CTAP_residentKey));
printf1(TAG_GREEN, "overwriting RK %d @ page %d @ addr 0x%08x-0x%08x\r\n",
index, RK_START_PAGE + page_offset,
flash_addr(RK_START_PAGE + page_offset) + byte_offset_into_page,
flash_addr(RK_START_PAGE + page_offset) + byte_offset_into_page + sizeof(CTAP_residentKey)
);
printf1(TAG_GREEN, "overwriting RK %d\r\n", index);
if (page_offset < RK_NUM_PAGES)
{
memmove(tmppage, (uint8_t*)flash_addr(RK_START_PAGE + page_offset), PAGE_SIZE);
memmove(tmppage, (uint8_t*)flash_addr(page), PAGE_SIZE);
memmove(tmppage + byte_offset_into_page, rk, sizeof(CTAP_residentKey));
flash_erase_page(RK_START_PAGE + page_offset);
flash_write(flash_addr(RK_START_PAGE + page_offset), tmppage, PAGE_SIZE);
memmove(tmppage + (sizeof(CTAP_residentKey) * index) % PAGE_SIZE, rk, sizeof(CTAP_residentKey));
flash_erase_page(page);
flash_write(flash_addr(page), tmppage, PAGE_SIZE);
}
else
{
printf2(TAG_ERR,"Out of bounds reading index %d for rk\n", index);
}
printf1(TAG_GREEN, "4\r\n");
}
void boot_st_bootloader(void)