Skip to content

Commit

Permalink
Add command AT$LOCKKEYS
Browse files Browse the repository at this point in the history
This AT command can be used to prevent the application from reading
LoRaWAN security keys over the AT command interface. After this command
has been invoked, any attempt to read LoRaWAN security keys via the ATCI
will return an error (-50, access denied). This setting will remain in
effect until the next reset to factory defaults.

Note: This command provides only minimal accidental protection. If the
application has access to USART1, USART2, or SPI ports on the modem, it
could simply downgrade the modem's firmware to read the keys, or it
could switch into the STM32 bootloader mode to directly read the EEPROM.
LoRaWAN security keys are stored in the EEPROM in an unencrypted form.

This patch represents the first step towards securing LoRaWAN security
keys. Future patches may implement additional protection, e.g., store
the security keys in the EEPROM in an encrypted form.

Related to #121
  • Loading branch information
janakj committed Dec 28, 2022
1 parent 7e3bfab commit dc2427c
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 4 deletions.
27 changes: 26 additions & 1 deletion src/cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ typedef enum cmd_errno {
ERR_UNSUPPORTED = -17, // Not supported in the current band
ERR_DUTYCYCLE = -18, // Cannot transmit due to duty cycling
ERR_NO_CHANNEL = -19, // Channel unavailable due to LBT or error
ERR_TOO_MANY = -20 // Too many link check requests
ERR_TOO_MANY = -20, // Too many link check requests
ERR_ACCESS_DENIED = -50 // Read access to security keys is denied
} cmd_errno_t;


Expand Down Expand Up @@ -413,6 +414,8 @@ static void set_joineui(atci_param_t *param)

static void get_nwkskey(void)
{
if (sysconf.lock_keys) abort(ERR_ACCESS_DENIED);

atci_print("+OK=");

// We operate in a backwards-compatible 1.0 mode here and in that mode, the
Expand Down Expand Up @@ -464,6 +467,8 @@ static void set_nwkskey(atci_param_t *param)

static void get_appskey(void)
{
if (sysconf.lock_keys) abort(ERR_ACCESS_DENIED);

atci_print("+OK=");
atci_print_buffer_as_hex(find_key(APP_S_KEY), SE_KEY_SIZE);
EOL();
Expand All @@ -489,6 +494,8 @@ static void set_appskey(atci_param_t *param)

static void get_appkey(void)
{
if (sysconf.lock_keys) abort(ERR_ACCESS_DENIED);

atci_print("+OK=");
atci_print_buffer_as_hex(find_key(APP_KEY), SE_KEY_SIZE);
EOL();
Expand Down Expand Up @@ -1675,6 +1682,8 @@ static void do_halt(atci_param_t *param)

static void get_nwkkey(void)
{
if (sysconf.lock_keys) abort(ERR_ACCESS_DENIED);

atci_print("+OK=");
atci_print_buffer_as_hex(find_key(NWK_KEY), SE_KEY_SIZE);
EOL();
Expand All @@ -1700,6 +1709,8 @@ static void set_nwkkey(atci_param_t *param)

static void get_fnwksintkey(void)
{
if (sysconf.lock_keys) abort(ERR_ACCESS_DENIED);

atci_print("+OK=");
atci_print_buffer_as_hex(find_key(F_NWK_S_INT_KEY), SE_KEY_SIZE);
EOL();
Expand All @@ -1725,6 +1736,8 @@ static void set_fnwksintkey(atci_param_t *param)

static void get_snwksintkey(void)
{
if (sysconf.lock_keys) abort(ERR_ACCESS_DENIED);

atci_print("+OK=");
atci_print_buffer_as_hex(find_key(S_NWK_S_INT_KEY), SE_KEY_SIZE);
EOL();
Expand All @@ -1750,6 +1763,8 @@ static void set_snwksintkey(atci_param_t *param)

static void get_nwksenckey(void)
{
if (sysconf.lock_keys) abort(ERR_ACCESS_DENIED);

atci_print("+OK=");
atci_print_buffer_as_hex(find_key(NWK_S_ENC_KEY), SE_KEY_SIZE);
EOL();
Expand Down Expand Up @@ -2083,6 +2098,15 @@ static void nvm_userdata(atci_param_t *param)
}


static void lock_keys(atci_param_t *param)
{
(void)param;
sysconf.lock_keys = 1;
sysconf_modified = true;
OK_();
}


static const atci_command_t cmds[] = {
{"+UART", NULL, set_uart, get_uart, NULL, "Configure UART interface"},
{"+VER", NULL, NULL, get_version_comp, NULL, "Firmware version and build time"},
Expand Down Expand Up @@ -2154,6 +2178,7 @@ static const atci_command_t cmds[] = {
{"$CW", cw, NULL, NULL, NULL, "Start continuous carrier wave transmission"},
{"$CM", cm, NULL, NULL, NULL, "Start continuous modulated FSK transmission"},
{"$NVM", nvm_userdata, NULL, NULL, NULL, "Manage data in NVM user registers"},
{"$LOCKKEYS", lock_keys, NULL, NULL, NULL, "Prevent read access to security keys from ATCI"},
ATCI_COMMAND_CLAC,
ATCI_COMMAND_HELP};

Expand Down
1 change: 1 addition & 0 deletions src/nvm.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ sysconf_t sysconf = {
.default_port = 2,
.data_format = 0,
.sleep = 1,
.lock_keys = 0,
.device_class = CLASS_A,
.unconfirmed_retransmissions = 1,
.confirmed_retransmissions = 8
Expand Down
13 changes: 10 additions & 3 deletions src/nvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,26 @@ typedef struct sysconf
* indicates that the client submits payloads in binary form. The value 1
* indicates that the client submits payloads in a hex form.
*/
uint8_t data_format;
uint8_t data_format : 1;

/* This parameter controls whether the firmware enters low-power modes when
* idle. Set to 0 to disable all low-power modes, set to 1 to enable
* low-power modes.
*/
uint8_t sleep;
uint8_t sleep : 1;

/* We need to keep LoRa device class here, in addition to the MIB, because
* the MIB variable is reset to class A during Join. Having a separate copy
* here allows us to restore the class after Join.
*/
uint8_t device_class;
uint8_t device_class : 2;

/* When this flag is set to 1, the AT command interface will prevent the
* application from reading the various LoRaWAN security keys. The
* corresponding AT commands will return an error. This flag can be only
* reset back to 0 with a factory reset.
*/
uint8_t lock_keys : 1;

/* The maximum number of retransmissions of unconfirmed uplink messages.
* Receiving a downlink message from the network stops retransmissions.
Expand Down

0 comments on commit dc2427c

Please sign in to comment.