One setting names the keyboard layout. It is in force before the state partition is unlocked, because the passphrase is the first thing typed, and the same row of one table gives the console its keymap and the desktop its xkb layout. The name is kept in a firmware variable; the table and the keymaps are on the verified root.
The kernel's own keymap is a US one. At the passphrase prompt a German
keyboard's Z key then types y and its - key types /, so the passphrase
a person believes they set is not the one they typed. The desktop has the
same question with another answer: dwl compiles an xkb keymap, US unless it
is told otherwise, and hands it to every client of every zone.
The setting cannot live where settings live. The state partition is not open when the passphrase is asked. The root is one signed image for every machine. The kernel's command line is compiled into the signed kernel. What is left is unauthenticated whatever is chosen, so the choice is about what an attacker can do with it and what the boot has to parse to read it.
A file on the ESP. It travels with the disk and needs no firmware
support. But the ESP is the one partition anyone holding the disk can
rewrite, and reading a file from it means mounting it: the kernel would
parse a FAT filesystem of the attacker's making, as root, at every boot,
before the passphrase is typed. Today the running system mounts the ESP only
to commit a trial or apply an update, after the state is open. A flaw in
that parser would be kernel code execution at the passphrase prompt, which
is what the offline attacker of the threat model wants
most. A whole keymap on the ESP would be worse than a name: loadkeys would
parse it too, and a keymap can bind keys to strings and to console actions.
A firmware variable. efivarfs hands over a few bytes behind four of
attributes; there is no filesystem to parse. Writing it takes code running
on the machine, root on the installed system or the medium's shell: the disk
in hand is not enough. It belongs to the machine, not the disk, so a disk
moved to another machine, or a firmware reset, asks under us; and a
firmware's variable store is finite, though an update already writes boot
entries there.
Nothing kept: a list on the verified root alone. The layout would be the builder's choice, one for every machine, or the user's at every boot, picked blind before a passphrase that shows no echo.
The variable holds the name and the root holds everything else. A name counts only if the table on the verified root lists it, and what is loaded is that row's keymap from the verified root: the variable's bytes pick a row and never reach a command line.
What changing the variable gains. Whoever can write it (another system
booted on the machine, the medium's shell, firmware setup) can select
another layout the release ships, and nothing else. The passphrase typed
under it then fails to open the state: three tries and a degraded boot,
which is denial of service, and deleting the boot file does as much. Nothing
typed is disclosed, since the characters go to cryptsetup and nowhere
else. The prompt names the layout in force on every console, so the change
is in front of the user before they type.
A name the release does not have (a variable from another release or
another system, or bytes that are no name) loads us, and the prompt says
both: that the firmware names a layout this release does not have, and that
the layout is us.
firmware variable verified root in force
KryptikKeyboard = "de" ──▶ keyboard-layouts: de → console keymap ──▶ loadkeys, before the passphrase
→ xkb layout ──▶ /run/kryptik-keyboard ──▶ dwl
The table. /usr/share/kryptik/keyboard-layouts, from
build/config/keyboard-layouts: a name, a console keymap of kbd under
/usr/share/keymaps, an xkb layout and its variant. us is first. Stage 04
parses every row's keymap with loadkeys and looks up every xkb layout and
variant in xkeyboard-config: a row that fails either fails the build.
The variable. KryptikKeyboard under Kryptik's own GUID
(ec0aed97-b78d-446f-997d-10d0c35f5fb6), non-volatile, holding the name in
ASCII: at most 32 bytes of lower-case letters, digits and -. No variable
means us.
At boot. sysinit, on an installed system and before its first
question, reads the variable, loads the row's keymap and says
sysinit: keyboard layout NAME on every console. It records the row in
/run/kryptik-keyboard, which a session's user can read. A medium does none
of this and keeps the kernel's keymap: it is the same medium on every
machine, and its shell is where a wrong name gets repaired.
Installing. kryptik-install --keyboard NAME loads the layout before
anything is typed, so ERASE and the new passphrase are typed under the
layout the installed system will ask under, and writes the name to the
variable before it writes to the disk. Without the option it records the
layout in force on the medium, us unless kryptik keyboard NAME changed
it there.
Changing it. kryptik keyboard lists the layouts and marks the one in
force. kryptik keyboard NAME, as root, loads it on the console and writes
the variable: the desktop has it at its next login and the passphrase prompt
at the next boot. On a medium it loads the keymap and records nothing.
The desktop. kryptik-session exports the row's XKB_DEFAULT_LAYOUT
and XKB_DEFAULT_VARIANT; dwl's rule names are empty, so libxkbcommon takes
them. Every zone's clients get the compositor's keymap through their proxy,
and no zone has a say in it.
The desktop's own layouts. A user may list up to four of the table's
names for the desktop alone, kryptik keyboard desktop us de writing them
to ~/.config/kryptik/keyboard; kryptik keyboard desktop machine goes
back to the machine's. The file is in the user's home, on the state
partition, which is fine for it: a keymap is no privilege. At the next
login kryptik-session exports the rows' layouts and variants as one
keymap's groups, the first in force, and grp:win_space_toggle, so
Super+Space switches. dwl's own keys are Alt's, and with Super held the
space key gives ISO_Next_Group, which no window takes as a character. A
list with a name the table lacks, or more than four, counts as none and the
machine's layout stands. Zones get the keymap and the group in force as
before. The screen lock names the layout in force under the marks, from
KRYPTIK_LAYOUTS, as a password typed in the other one fails. The state
passphrase at boot is asked under the machine's layout alone, before any
home exists.
The serial console is not touched: what arrives there was mapped by the far end's terminal.
tools/tests/keyboard.sh: the names the table takes and refuses, what the variable may hold (a name; other characters, 40 bytes, nothing, a file shorter than its attributes), what a load givesloadkeysand records, what the session exports, what a store writes byte for byte, and the shipped table's form; and a desktop list: two layouts with their variants and the switch, one, four, and a fifth name, an unknown name or an empty list leaving the machine's layout.make gui-testlogs its user in withus de: dwl's environment carries both, the screen lock names US and, after Super+Space, DE; the password typed in de is refused, and typed again after Super+Space, back on us, it opens the lock.- Stage 04 checks every row against the keymaps and xkb data it installed.
tools/image/keyboard-test.sh, in acceptance's install suite: an install with--keyboard de; the next boot names the layout and takes the passphrase as key presses on the guest's keyboard, placed where a German keyboard hasz,yand-; from inside, the console's Y key giveszand the session's record and export sayde;kryptik keyboardrefuses a name the table lacks and changes back tous, which the next boot says; and a variable holding a name the release lacks boots underusand says so.
- kbd's keymaps and xkb's layouts are two descriptions of a keyboard, paired by the table. They agree on the letters, digits and symbols printed on the keys; nothing checks that they agree on the AltGr level or on dead keys. A zone's volume passphrase is asked in the desktop, the state's on the console: one that uses those levels may type differently in the two.
- Latin layouts only, and no input method.
- The variable belongs to the machine: a disk moved to another one asks
under
usuntilkryptik keyboardis run there.
build/service-scripts/keyboard.sh, build/config/keyboard-layouts,
build/service-scripts/sysinit.sh, tools/install/kryptik-install.sh,
tools/kryptik (kryptik keyboard), tools/desktop/kryptik-session,
build/recipes/services.sh (the table and its check),
build/desktop/dwl-config.h (the empty rule names).