Co-authored-by: Drashna Jaelre <drashna@live.com> Co-authored-by: Stefan Kerkmann <karlk90@pm.me>pull/16863/head
@ -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; | |||
} |
@ -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); |
@ -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 | |||
} |
@ -0,0 +1,67 @@ | |||
// Copyright 2022 QMK | |||
// SPDX-License-Identifier: GPL-2.0-or-later | |||
#pragma once | |||
/** \file | |||
* | |||
* Exposes a set of functionality to act as a virtual padlock for your device | |||
* ... As long as that padlock is made of paper and its currently raining. | |||
*/ | |||
#include <stdint.h> | |||
#include <stdbool.h> | |||
/** \brief Available secure states | |||
*/ | |||
typedef enum { | |||
SECURE_LOCKED, | |||
SECURE_PENDING, | |||
SECURE_UNLOCKED, | |||
} secure_status_t; | |||
/** \brief Query current secure state | |||
*/ | |||
secure_status_t secure_get_status(void); | |||
/** \brief Helper to check if unlocking is currently locked | |||
*/ | |||
#define secure_is_locked() (secure_get_status() == SECURE_LOCKED) | |||
/** \brief Helper to check if unlocking is currently in progress | |||
*/ | |||
#define secure_is_unlocking() (secure_get_status() == SECURE_PENDING) | |||
/** \brief Helper to check if unlocking is currently unlocked | |||
*/ | |||
#define secure_is_unlocked() (secure_get_status() == SECURE_UNLOCKED) | |||
/** \brief Lock down the device | |||
*/ | |||
void secure_lock(void); | |||
/** \brief Force unlock the device | |||
* | |||
* \warning bypasses user unlock sequence | |||
*/ | |||
void secure_unlock(void); | |||
/** \brief Begin listening for an unlock sequence | |||
*/ | |||
void secure_request_unlock(void); | |||
/** \brief Flag to the secure subsystem that user activity has happened | |||
* | |||
* Call when some user activity has happened and the device should remain unlocked | |||
*/ | |||
void secure_activity_event(void); | |||
/** \brief Flag to the secure subsystem that user has triggered a keypress | |||
* | |||
* Call to trigger processing of the unlock sequence | |||
*/ | |||
void secure_keypress_event(uint8_t row, uint8_t col); | |||
/** \brief Handle various secure subsystem background tasks | |||
*/ | |||
void secure_task(void); |