My keyboard is everything to me. Like many growing up over the past 2 decades, time at the computer became a measure of existence within the digital world. From playing the hero in an adventure game, writing security programs and tooling to defend against the next dark cyber criminals, turning beats into a melody and more, when not in nature or in the workshop, easily over 12 hours a day are spent at a keyboard.

For some, a keyboard is a keyboard. For me, it’s my castle. Every keystroke is deliberately calculated in smooth combinations of finger movements like figure skaters pushing themselves harder and harder. Everything from the body, switches and keycaps are customized. Using QMK, an open-source keyboard firmware, I’m able to tweak and customize just about every aspect of my keyboard exactly how I need it. You can apply layers using modifier keys, meaning pressing Shift + J and Ctrl + J yield two different key combinations, without needing to run additional software on your computer.

Like a well balanced hammer, my keyboard teeters the line between striking blows building the foundation of the future through code, and being the first line in defense against adversaries. In order to do that, I changes how I perceived my keyboard. I treat it like my digital passport. Those who have them, treat their YubiKey’s like digital passports, and at the end of the day, regardless of the cryptographic strength inside, having physical access to it is everything. I applied the same concept to my keyboard. Using a modified version of the Leader Key concept, my passphrase and pin code macros are locked until unlocked. I might do a separate article on this if there is interest, but it essentially is a leader key sequence that activates a boolean variable. If it’s true, it will send_string_with_delay and will time out after 5 minutes using a timer.

Adding A Secrets’ Macro

I won’t go into the details of a keymap for QMK, if you’re unfamiliar, take a look at their GitHub README on it. Instead, we will focus on how to extend a keymap to add a custom key code.

Starting with the header files, we are going to create 2 new key codes, I called them E_PIN and E_PHRASE

keymap.h#include QMK_KEYBOARD_H#include <print.h>
#include <string.h>enum alt_keycodes {
 U_T_AUTO = SAFE_RANGE, //USB Extra Port Toggle Auto Detect / Always Active
 U_T_AGCR,              //USB Toggle Automatic GCR control
 DBG_TOG,               //DEBUG Toggle On / Off
 DBG_MTRX,              //DEBUG Toggle Matrix Prints
 DBG_KBD,               //DEBUG Toggle Keyboard Prints
 DBG_MOU,               //DEBUG Toggle Mouse Prints
 MD_BOOT,               //Restart into bootloader after hold timeout
 CHNG_MD,               //
 TERMINAL,              //
};enum string_secret_keycodes {
 // The start of this enum should always be equal to end of alt_keycodes + 1
 E_PIN = TERMINAL + 1, // PIN
 E_PHRASE,             // PHRASE
};

These two new key codes are simply placeholders of an offset which can be registered and have events mapped to. If we never add them to the keymap matrix, they exist rent free in the firmware for all but a few bits.

Next, we are going to move onto defining the values of our phrase and pin code within keymap.c.

keymap.c#define SECRET_PIN "8675309"
#define SECRET_PHRASE "YOURMOM"static const char * cmds[] = {
   SECRET_PIN,
   SECRET_PHARSE,
};

Also within this file, we need to add an event check within process_record_user which detects when a key was pressed or raised. Using the enum values we assigned in the header, we will check for the range of our secret key codes. We also use the offset of the pressed key code subtracted from the secret macros key codes to get the index value from within the cmd array. This is the only math that mattered to me in high school.case E_PIN ... E_PHRASE:
   if (record->event.pressed) {
       send_string_with_delay(cmds[keycode - E_PIN], 1);
       register_code(KC_ENT);
       unregister_code(KC_ENT);
   }
   break;

You may have also caught on to where I added an extra Enter after the shortcut was pressed to auto-submit the value, and saves an additional manual keystroke.

When we put it all together, our keymap.c file starts to look a little something like this:


Keeping The Secrets Safe

If you’re like me, you have your own .dotfiles repository and keep all your configurations there for every new system you log into. It’s a great practice, and reduces the need to manually copy configurations. However, let’s say you want to store your cool new password QMK changes without committing them to your repository. There is a solution for that as well!

The __has_include operator is a compile check for if the included resource is available or not. If it is, it will execute the inner instructions. Otherwise, it will skip. What this allows us to do is isolate our secrets in a secrets.h file, which would be added to your .gitignore.

Read more about __has_include below:

__has_include (The C Preprocessor)
4.2.10 __has_include The special operator __has_include (operand) may be used in ‘#if’ and ‘#elif’ expressions to test…

To do this, we would add the header file check into our keymap.c and set default values for the SECRET_PIN and SECRET_PHRASE in the event a secrets.h file is not present, allowing it to successfully compile.#if __has_include("secrets.h")
# include "secrets.h"
#endif#ifndef SECRET_PIN
 #define SECRET_PIN "ok"
#endif#ifndef SECRET_PHARSE
 #define SECRET_PHARSE "ok"
#endif

secrets.h#define SECRET_PIN "8675309"
#define SECRET_PHARSE "YOURM"
.gitignoresecrets.h

I also came to the realization that I do not like sentences that start with a capital B.

Share this post