Padlock is a Linux keystore. It stores a hidden append-only key/value file in each user's home directory, encrypts the store at rest, and keeps older key/value pairs in place so reads return the last matching entry.
- Creates a fixed-size store file, for example
~/.padlock/store.plk. - Can also place the encrypted
store.plkinside a mounted disk image, for example/enclave/keystore/store.plk. - Fills unused space with random bytes.
- Encrypts the store data with AES-256-XTS.
- Seals a helper secret through TPM data in
~/.padlock/tpm/; that helper is used to derive the header password, which protects the store key. - Uses direct TPM2 ESAPI calls to create, load, and unseal the sealed helper secret.
- Prompts once for the user's Linux password, verifies it through PAM, and derives the header password through the TPM-backed helper path.
- Appends key/value pairs instead of overwriting them.
- Reads scan to the end of the file so duplicate keys resolve to the most recent value.
Padlock is meant to be the local keystore layer for Hardcode authentication and credential workflows. The intent is to keep sensitive secrets encrypted at rest, tied to the local machine through TPM state, while still letting the user authenticate through the standard Linux login path.
The installer script auto-detects apt-get or dnf and installs the matching build dependencies before building and loading Padlock.
On AWS Linux, use ./install.sh --aws-linux to enable the arm64-safe kernel guard build path.
Use --user <name> to add a specific user to the tpmadm group.
Use --auth-no-prompt with --user <name> to install a pam_succeed_if.so
rule that trusts that specific user without prompting for a password.
Use --disk-img to back the keystore with a root-owned, immutable disk.img
that is mounted through /etc/fstab with nodev,nosuid,noexec.
Use --enclave <size> to create the enclave user, build Padlock for the
enclave workflow, and allocate an encrypted keystore of the requested size
inside /enclave/keystore.
The installer autodetects the running kernel release and looks for headers in
/lib/modules/$(uname -r)/build first, then /usr/src/kernels/$(uname -r).
If neither exists, it stops and tells you to install the matching kernel
headers.
sudo apt-get update
sudo apt-get install -y build-essential cmake libssl-dev libpam0g-dev linux-headers-$(uname -r) \
libtss2-esys-3.0.2-0t64 libtss2-mu-4.0.1-0t64 libtss2-tctildr0t64 libtss2-tcti-device0t64build-essentialprovidescc,make, and the C toolchain.cmakeprovides the project generator and build orchestration.libssl-devprovides the OpenSSL headers and libraries used for AES and HMAC/KDF work.libpam0g-devprovides the PAM headers used to validate the login password.libtss2-esys-3.0.2-0t64,libtss2-mu-4.0.1-0t64,libtss2-tctildr0t64, andlibtss2-tcti-device0t64provide the TPM2 runtime libraries used by the direct ESAPI path.linux-headers-$(uname -r)provides the headers needed to build the guard module.- The machine needs access to
/dev/tpmrm0or/dev/tpm0. - The user running
padlockmust have access to thetpmadmgroup on the active session.
sudo dnf install -y gcc make cmake openssl-devel pam-devel kernel-devel kmod tpm2-tss-develNotes:
gccandmakeprovide the C toolchain and build driver.cmakeprovides the project generator and build orchestration.openssl-develprovides the OpenSSL headers and libraries used for AES and HMAC/KDF work.pam-develprovides the PAM headers used to validate the login password.tpm2-tss-develprovides the TPM2 headers and libraries used by the direct ESAPI path.kernel-develprovides the headers needed to build the guard module.kmodprovidesinsmod,lsmod, andrmmodif they are not already present.- The machine needs access to
/dev/tpmrm0or/dev/tpm0. - The user running
padlockmust have access to thetpmadmgroup on the active session.
cmake -S apps/padlock -B apps/padlock/build
cmake --build apps/padlock/buildThis produces:
apps/padlock/build/libpadlock.soapps/padlock/build/libpadlock.aapps/padlock/build/padlock
cmake --build apps/padlock/build --target guardThis produces:
apps/padlock/kernel/guard/hardcode_padlock_guard.ko
The build target leaves the module in the source tree. Load it directly with insmod:
sudo insmod apps/padlock/kernel/guard/hardcode_padlock_guard.koVerify that it loaded and created the device node:
lsmod | rg hardcode_padlock_guard
ls -l /dev/hardcode_padlock_guardTo unload it:
sudo rmmod hardcode_padlock_guardmodprobe hardcode_padlock_guard only works after the module has been installed into /lib/modules/$(uname -r) and depmod -a has been run. If you are testing from a build tree, use insmod against the built .ko file.
ctest --test-dir apps/padlock/build --output-on-failureThe test suite exercises:
- constant-time compare helpers
- secure zeroing
- allocation of a new store
- appending key/value pairs
- duplicate-key reads returning the last stored value
Preferred setup for a fresh machine:
./apps/padlock/install.shThat script installs the dependencies, writes the PAM service, builds Padlock, installs the binaries, adds the user to tpmadm, and loads the guard module.
On AWS Linux or Amazon Linux, use:
./apps/padlock/install.sh --aws-linuxThat variant installs the dependencies, writes the PAM service, builds Padlock, installs the binaries, adds the user to tpmadm, and builds and loads the guard module using the AWS Linux compatibility path.
To install Padlock for a specific user without a password prompt:
./apps/padlock/install.sh --user ryan --auth-no-promptThat variant writes /etc/pam.d/padlock with a single pam_succeed_if.so
rule for the chosen user. Run padlock as that same OS user.
To create the enclave user and a disk-backed keystore in one step:
./apps/padlock/install.sh --aws-linux --enclave 2GThat mode creates a system user named enclave with home directory
/enclave, installs Padlock with --user enclave --auth-no-prompt --disk-img, creates a root-owned immutable disk.img, mounts it at
/enclave/keystore, allocates the encrypted store.plk, and installs a
wrapper that runs padlock get and padlock set through sudo -u enclave.
Configure and build first:
cmake -S apps/padlock -B apps/padlock/build
cmake --build apps/padlock/buildThen install with an explicit prefix:
cmake --install apps/padlock/build --prefix /usrTo stage into a package root, use DESTDIR with the install step:
DESTDIR=/tmp/stage cmake --install apps/padlock/build --prefix /usrTo remove a Padlock install from the machine, use:
./apps/padlock/uninstall.shThat script removes the /usr/local install, the PAM and udev rules, the
loaded kernel guard module if present, the known enclave disk-image
artifacts, and the enclave user plus tpmadm group if they exist. Pass
--keep-build if you want to leave the local build/ directory in place.
The install step places:
libpadlock.soin/usr/libor/usr/lib64, depending on the platformlibpadlock.ain/usr/libor/usr/lib64, depending on the platformpadlockin/usr/binhardcode/padlock.hin/usr/include/hardcodehardcode/padlock_guard.hin/usr/include/hardcode
The installed padlock binary is configured to find libpadlock.so.0 in the
matching sibling lib or lib64 directory for the chosen prefix, so you
should not need to set LD_LIBRARY_PATH manually.
The TPM helper path opens /dev/tpmrm0 first and falls back to /dev/tpm0.
The installer writes a udev rule at /etc/udev/rules.d/60-padlock-tpm.rules
that keeps TPM devices at 0660 with owner tss and group tpmadm. It also
adds the current user, and any members of the root group, to tpmadm. Add a
specific user to that group manually if you want:
sudo usermod -aG tpmadm ryanThe new group membership does not apply to an existing shell session. Log out and log back in, or start a fresh session with:
newgrp tpmadmYou can verify access with:
id
ls -l /dev/tpmrm0 /dev/tpm0The padlock CLI should use a dedicated PAM service named padlock with a password-only stack. This avoids inheriting the host common-auth stack, which can pull in modules such as pam_pkcs11 and block password verification even when the Unix password is correct.
Create /etc/pam.d/padlock with:
auth required pam_unix.so
account required pam_unix.so
Make sure the file is readable by the CLI at runtime:
sudo chown root:root /etc/pam.d/padlock
sudo chmod 0644 /etc/pam.d/padlockIf your distribution already has a clean password-only PAM include that you trust, you can adapt the service to use it. Do not point padlock at a stack that requires smart cards or other hardware tokens unless that is explicitly what you want.
If you install with --user <name> --auth-no-prompt, the PAM service is reduced to:
auth required pam_succeed_if.so user = <name>
In that mode, the CLI does not prompt for a password and derives the header password without prompting for a login password.
When you install with --disk-img, Padlock stores the encrypted store.plk
inside a mounted loop device instead of directly under the user home
directory. The installer:
- creates
disk.imgunder the target user's.padlock/directory - formats it as ext4
- writes an
/etc/fstabentry that does not use theuseroption, so only root can mount it - mounts it with
nodev,nosuid,noexec - sets the backing file to
root:root,0600, and immutable withchattr +i
The mounted keystore directory is the path used by Padlock at runtime, for
example /enclave/keystore.
When you install with --enclave <size>, the installer combines the disk image
mode with the enclave user workflow:
- creates the
enclavesystem user with home/enclaveand shell/sbin/nologin - builds and installs Padlock for
--user enclave --auth-no-prompt --disk-img - allocates the encrypted
store.plkasenclave - installs a
padlockwrapper that only supportsgetandset - prepends
sudo -u enclavebefore the real binary so regular sudo authentication is used to becomeenclave
The guard module exposes /dev/hardcode_padlock_guard and uses ioctl calls to:
- register a file as protected
- begin a write session for the current
tgid - end that write session
- query the current protection state
While a protected file has no active write session, the module denies write permission, truncation, unlink, and rename attempts for that inode.
Padlock resolves the store path in this order:
- If
PADLOCK_STORE_ROOTis set, it uses<store-root>/store.plk. - Otherwise it uses
~/.padlock/store.plk.
padlock allocate 2GBOr specify a custom path for the standalone CLI:
padlock allocate 2GB /home/ryan/.padlock/custom.plkpadlock set api_key "super-secret-value"With an explicit path for the standalone CLI:
padlock set api_key "super-secret-value" /home/ryan/.padlock/custom.plkpadlock get api_keyThe command prompts once for the Linux password, authenticates it with PAM, derives the header password, unseals the TPM-backed secret, and returns the most recent matching value.
When Padlock is installed with --enclave, the wrapper only supports:
padlock get <key>
padlock set <key> <value>In enclave mode the wrapper prepends sudo -u enclave, sets PADLOCK_STORE_ROOT
to /enclave/keystore, and rejects allocate and custom path arguments.
~/.padlock/store.plk- encrypted keystore file/enclave/keystore/store.plk- encrypted keystore file when installed with--enclave~/.padlock/disk.img- loop-backed disk image when installed with--disk-img~/.padlock/tpm/header-hmac.pub- TPM public blob for the sealed secret~/.padlock/tpm/header-hmac.priv- TPM private blob for the sealed secret- When
PADLOCK_STORE_ROOTis set,store.plklives directly under that root.
padlock: authentication: Permission deniedusually means the PAM stack is not password-only, or the typed password was rejected by the active PAM policy.TPM access denied while deriving header passwordusually means the active session is not in thetpmadmgroup yet.Esys_Create() ... inconsistent attributesmeans the TPM object template is wrong; this was fixed by removing the caller-incompatibleSENSITIVEDATAORIGINattribute from the sealed object template.- If
command -v padlockshows/usr/local/bin/padlock, make sure you installed the updated binary there as well as under/usr/bin, or call the binary explicitly by path.
This is a working prototype, not a finished security product. The remaining work is:
- Define rotation and backup behavior for the sealed secret blobs, the store file, and any backing disk image.
- Add corruption detection and recovery for truncated or partially-written stores.
- Add a real policy for key deletion, record compaction, and store growth.
- Add a service wrapper or helper for boot-time store mount/unseal if you want the file loaded before the first lookup.
- The TPM-sealed secret is only a helper used during header password derivation. The store key itself is still protected by the encrypted header.
- The user password is still the security boundary for the header. TPM protects the secret that HMACs that password; it does not make a weak password strong.
- The current implementation uses PAM to verify the typed password once and direct TPM2 ESAPI calls to create, load, and unseal the TPM blob.
- The kernel guard module is a hardening layer for registered files, not a defense against a compromised kernel or a user who can bypass the guard device.
- The TPM device must still be reachable from the current user or through system permissions for the direct ESAPI path to work.