Skip to content

Commit

Permalink
Implement XAP 'secure' core requirements (#16843)
Browse files Browse the repository at this point in the history
Co-authored-by: Drashna Jaelre <[email protected]>
Co-authored-by: Stefan Kerkmann <[email protected]>
  • Loading branch information
3 people authored Apr 16, 2022
1 parent ae4d518 commit 92a61aa
Show file tree
Hide file tree
Showing 15 changed files with 312 additions and 13 deletions.
1 change: 1 addition & 0 deletions builddefs/generic_features.mk
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ GENERIC_FEATURES = \
KEY_OVERRIDE \
LEADER \
PROGRAMMABLE_BUTTON \
SECURE \
SPACE_CADET \
SWAP_HANDS \
TAP_DANCE \
Expand Down
3 changes: 2 additions & 1 deletion builddefs/show_options.mk
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ OTHER_OPTION_NAMES = \
LED_MIRRORED \
RGBLIGHT_FULL_POWER \
LTO_ENABLE \
PROGRAMMABLE_BUTTON_ENABLE
PROGRAMMABLE_BUTTON_ENABLE \
SECURE_ENABLE

define NAME_ECHO
@printf " %-30s = %-16s # %s\\n" "$1" "$($1)" "$(origin $1)"
Expand Down
3 changes: 3 additions & 0 deletions data/mappings/info_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@
"QMK_KEYS_PER_SCAN": {"info_key": "qmk.keys_per_scan", "value_type": "int"},
"QMK_LED": {"info_key": "qmk_lufa_bootloader.led"},
"QMK_SPEAKER": {"info_key": "qmk_lufa_bootloader.speaker"},
"SECURE_UNLOCK_SEQUENCE": {"info_key": "secure.unlock_sequence", "value_type": "array.array.int", "to_json": false},
"SECURE_UNLOCK_TIMEOUT": {"info_key": "secure.unlock_timeout", "value_type": "int"},
"SECURE_IDLE_TIMEOUT": {"info_key": "secure.idle_timeout", "value_type": "int"},
"SENDSTRING_BELL": {"info_key": "audio.macro_beep", "value_type": "bool"},
"SPLIT_MODS_ENABLE": {"info_key": "split.transport.sync_modifiers", "value_type": "bool"},
"SPLIT_TRANSPORT_MIRROR": {"info_key": "split.transport.sync_matrix_state", "value_type": "bool"},
Expand Down
1 change: 1 addition & 0 deletions data/mappings/info_rules.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"MOUSEKEY_ENABLE": {"info_key": "mouse_key.enabled", "value_type": "bool"},
"NO_USB_STARTUP_CHECK": {"info_key": "usb.no_startup_check", "value_type": "bool"},
"PIN_COMPATIBLE": {"info_key": "pin_compatible"},
"SECURE_ENABLE": {"info_key": "secure.enabled", "value_type": "bool"},
"SPLIT_KEYBOARD": {"info_key": "split.enabled", "value_type": "bool"},
"SPLIT_TRANSPORT": {"info_key": "split.transport.protocol", "to_c": false},
"WAIT_FOR_USB": {"info_key": "usb.wait_for", "value_type": "bool"}
Expand Down
24 changes: 24 additions & 0 deletions data/schemas/keyboard.jsonschema
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,30 @@
}
}
},
"secure": {
"type": "object",
"additionalProperties": false,
"properties": {
"enabled": {"type": "boolean"},
"unlock_timeout": {"$ref": "qmk.definitions.v1#/unsigned_int"},
"idle_timeout": {"$ref": "qmk.definitions.v1#/unsigned_int"},
"unlock_sequence": {
"type": "array",
"minLength": 1,
"maxLength": 5,
"items": {
"type": "array",
"minItems": 2,
"maxItems": 2,
"items": {
"type": "number",
"min": 0,
"multipleOf": 1
}
}
}
}
},
"split": {
"type": "object",
"additionalProperties": false,
Expand Down
7 changes: 6 additions & 1 deletion lib/python/qmk/cli/generate/config_h.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,12 @@ def generate_config_items(kb_info_json, config_h_lines):
except KeyError:
continue

if key_type.startswith('array'):
if key_type.startswith('array.array'):
config_h_lines.append('')
config_h_lines.append(f'#ifndef {config_key}')
config_h_lines.append(f'# define {config_key} {{ {", ".join(["{" + ",".join(list(map(str, x))) + "}" for x in config_value])} }}')
config_h_lines.append(f'#endif // {config_key}')
elif key_type.startswith('array'):
config_h_lines.append('')
config_h_lines.append(f'#ifndef {config_key}')
config_h_lines.append(f'# define {config_key} {{ {", ".join(map(str, config_value))} }}')
Expand Down
56 changes: 45 additions & 11 deletions lib/python/qmk/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,28 +168,46 @@ def _extract_pins(pins):
return [_pin_name(pin) for pin in pins.split(',')]


def _extract_direct_matrix(direct_pins):
"""
def _extract_2d_array(raw):
"""Return a 2d array of strings
"""
direct_pin_array = []
out_array = []

while direct_pins[-1] != '}':
direct_pins = direct_pins[:-1]
while raw[-1] != '}':
raw = raw[:-1]

for row in direct_pins.split('},{'):
for row in raw.split('},{'):
if row.startswith('{'):
row = row[1:]

if row.endswith('}'):
row = row[:-1]

direct_pin_array.append([])
out_array.append([])

for val in row.split(','):
out_array[-1].append(val)

return out_array


def _extract_2d_int_array(raw):
"""Return a 2d array of ints
"""
ret = _extract_2d_array(raw)

return [list(map(int, x)) for x in ret]

for pin in row.split(','):
if pin == 'NO_PIN':
pin = None

direct_pin_array[-1].append(pin)
def _extract_direct_matrix(direct_pins):
"""extract direct_matrix
"""
direct_pin_array = _extract_2d_array(direct_pins)

for i in range(len(direct_pin_array)):
for j in range(len(direct_pin_array[i])):
if direct_pin_array[i][j] == 'NO_PIN':
direct_pin_array[i][j] = None

return direct_pin_array

Expand All @@ -207,6 +225,21 @@ def _extract_audio(info_data, config_c):
info_data['audio'] = {'pins': audio_pins}


def _extract_secure_unlock(info_data, config_c):
"""Populate data about the secure unlock sequence
"""
unlock = config_c.get('SECURE_UNLOCK_SEQUENCE', '').replace(' ', '')[1:-1]
if unlock:
unlock_array = _extract_2d_int_array(unlock)
if 'secure' not in info_data:
info_data['secure'] = {}

if 'unlock_sequence' in info_data['secure']:
_log_warning(info_data, 'Secure unlock sequence is specified in both config.h (SECURE_UNLOCK_SEQUENCE) and info.json (secure.unlock_sequence) (Value: %s), the config.h value wins.' % info_data['secure']['unlock_sequence'])

info_data['secure']['unlock_sequence'] = unlock_array


def _extract_split_main(info_data, config_c):
"""Populate data about the split configuration
"""
Expand Down Expand Up @@ -466,6 +499,7 @@ def _extract_config_h(info_data, config_c):
# Pull data that easily can't be mapped in json
_extract_matrix_info(info_data, config_c)
_extract_audio(info_data, config_c)
_extract_secure_unlock(info_data, config_c)
_extract_split_main(info_data, config_c)
_extract_split_transport(info_data, config_c)
_extract_split_right_pins(info_data, config_c)
Expand Down
4 changes: 4 additions & 0 deletions quantum/keyboard.c
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,10 @@ void quantum_task(void) {
#ifdef AUTO_SHIFT_ENABLE
autoshift_matrix_scan();
#endif

#ifdef SECURE_ENABLE
secure_task();
#endif
}

/** \brief Keyboard task: Do keyboard routine jobs
Expand Down
39 changes: 39 additions & 0 deletions quantum/process_keycode/process_secure.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright 2022 QMK
// SPDX-License-Identifier: GPL-2.0-or-later

#include "secure.h"
#include "process_secure.h"
#include "quantum_keycodes.h"

bool preprocess_secure(uint16_t keycode, keyrecord_t *record) {
if (secure_is_unlocking()) {
if (!record->event.pressed) {
secure_keypress_event(record->event.key.row, record->event.key.col);
}

// Normal keypresses should be disabled until the sequence is completed
return false;
}

return true;
}

bool process_secure(uint16_t keycode, keyrecord_t *record) {
#ifndef SECURE_DISABLE_KEYCODES
if (!record->event.pressed) {
if (keycode == SECURE_LOCK) {
secure_lock();
return false;
}
if (keycode == SECURE_UNLOCK) {
secure_unlock();
return false;
}
if (keycode == SECURE_TOGGLE) {
secure_is_locked() ? secure_unlock() : secure_lock();
return false;
}
}
#endif
return true;
}
15 changes: 15 additions & 0 deletions quantum/process_keycode/process_secure.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2022 QMK
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <stdbool.h>
#include "action.h"

/** \brief Intercept keycodes and detect unlock sequences
*/
bool preprocess_secure(uint16_t keycode, keyrecord_t *record);

/** \brief Handle any secure specific keycodes
*/
bool process_secure(uint16_t keycode, keyrecord_t *record);
9 changes: 9 additions & 0 deletions quantum/quantum.c
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,12 @@ bool process_record_quantum(keyrecord_t *record) {
// return false;
// }

#if defined(SECURE_ENABLE)
if (!preprocess_secure(keycode, record)) {
return false;
}
#endif

#ifdef VELOCIKEY_ENABLE
if (velocikey_enabled() && record->event.pressed) {
velocikey_accelerate();
Expand Down Expand Up @@ -247,6 +253,9 @@ bool process_record_quantum(keyrecord_t *record) {
process_record_via(keycode, record) &&
#endif
process_record_kb(keycode, record) &&
#if defined(SECURE_ENABLE)
process_secure(keycode, record) &&
#endif
#if defined(SEQUENCER_ENABLE)
process_sequencer(keycode, record) &&
#endif
Expand Down
5 changes: 5 additions & 0 deletions quantum/quantum.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,11 @@ extern layer_state_t layer_state;
# include "process_dynamic_macro.h"
#endif

#ifdef SECURE_ENABLE
# include "secure.h"
# include "process_secure.h"
#endif

#ifdef DYNAMIC_KEYMAP_ENABLE
# include "dynamic_keymap.h"
#endif
Expand Down
4 changes: 4 additions & 0 deletions quantum/quantum_keycodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,10 @@ enum quantum_keycodes {

QK_MAKE,

SECURE_LOCK,
SECURE_UNLOCK,
SECURE_TOGGLE,

// Start of custom keycode range for keyboards and keymaps - always leave at the end
SAFE_RANGE
};
Expand Down
87 changes: 87 additions & 0 deletions quantum/secure.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright 2022 QMK
// SPDX-License-Identifier: GPL-2.0-or-later

#include "secure.h"
#include "timer.h"

#ifndef SECURE_UNLOCK_TIMEOUT
# define SECURE_UNLOCK_TIMEOUT 5000
#endif

#ifndef SECURE_IDLE_TIMEOUT
# define SECURE_IDLE_TIMEOUT 60000
#endif

#ifndef SECURE_UNLOCK_SEQUENCE
# define SECURE_UNLOCK_SEQUENCE \
{ \
{ 0, 0 } \
}
#endif

static secure_status_t secure_status = SECURE_LOCKED;
static uint32_t unlock_time = 0;
static uint32_t idle_time = 0;

secure_status_t secure_get_status(void) {
return secure_status;
}

void secure_lock(void) {
secure_status = SECURE_LOCKED;
}

void secure_unlock(void) {
secure_status = SECURE_UNLOCKED;
idle_time = timer_read32();
}

void secure_request_unlock(void) {
if (secure_status == SECURE_LOCKED) {
secure_status = SECURE_PENDING;
unlock_time = timer_read32();
}
}

void secure_activity_event(void) {
if (secure_status == SECURE_UNLOCKED) {
idle_time = timer_read32();
}
}

void secure_keypress_event(uint8_t row, uint8_t col) {
static const uint8_t sequence[][2] = SECURE_UNLOCK_SEQUENCE;
static const uint8_t sequence_len = sizeof(sequence) / sizeof(sequence[0]);

static uint8_t offset = 0;
if ((sequence[offset][0] == row) && (sequence[offset][1] == col)) {
offset++;
if (offset == sequence_len) {
offset = 0;
secure_unlock();
}
} else {
offset = 0;
secure_lock();
}
}

void secure_task(void) {
#if SECURE_UNLOCK_TIMEOUT != 0
// handle unlock timeout
if (secure_status == SECURE_PENDING) {
if (timer_elapsed32(unlock_time) >= SECURE_UNLOCK_TIMEOUT) {
secure_lock();
}
}
#endif

#if SECURE_IDLE_TIMEOUT != 0
// handle idle timeout
if (secure_status == SECURE_UNLOCKED) {
if (timer_elapsed32(idle_time) >= SECURE_IDLE_TIMEOUT) {
secure_lock();
}
}
#endif
}
Loading

0 comments on commit 92a61aa

Please sign in to comment.