PCR-ORACLE(8)
๐ PCR-ORACLE(8) ๐
okay bestie, welcome to pcr-oracle โ source file PCR-ORACLE(8), and like the tagline literally says: Predict TPM PCR values and Seal Secrets. that's the whole vibe. let's get into it ๐ซถ
โจ NAME โจ
pcr-oracle โ predict TPM PCR values and seal secrets ๐
โจ SYNOPSIS โจ
pcr-oracle [options] action ...
โจ DESCRIPTION โจ
okay so this utility is genuinely iconic. it can predict TPM PCR values at a specific time during system boot, create a PCR policy for those values, and then seal a secret to that policy. like, the whole pipeline. slay.
In simple prediction mode, pcr-oracle will compute expected hash values for a subset of PCRs, and write them to standard output. The most common use case will probably be to base the prediction on the TPM event log that was generated by the firmware during the most recent boot โ but ngl, there are other modes as well. it's flexible like that ๐
In sealing mode, pcr-oracle will seal a given secret (such as a LUKS device password) to either a set of PCR values, or to an authorized PCR policy. two paths, both valid, pick your fighter.
๐ PCR Policies vs Authorized Policies ๐
okay so this is the part where it gets good, like fr.
The simplest approach to sealing a secret against an expected system state using the TPM is to create what is called a "PCR Policy". This policy essentially requires a given set of PCRs to contain a specific set of values. This policy can be used to "seal" a secret, i.e. encrypt it using a key only the TPM has access to. main character energy: the TPM holds the key, nobody else does ๐ฅ
At some point, for example during the boot process, the boot loader will attempt to unseal this sealed secret. If the TPM's PCRs contain the expected values at this point in time, unsealing will succeed. If they do not contain the expected values, unsealing will either fail, or produce gibberish. like, literally nonsense bytes. ๐
While regular PCR policies are sound and simple, they are not very flexible, unfortunately. For example, transparently handling updates of the boot loader is rather difficult because we would first have to retrieve the secret before we're able to re-seal it to the future set of PCR values. annoying. this is where Authorized Policies come in โจ
TPM Authorized Policies provide a mechanism by which a secret is not sealed to a set of specific PCR values, but to a list of PCR indices, and a public RSA key. This combination (PCR indices + public key) is called an Authorized Policy. cute, right?
The owner of the system can then use the secret RSA key to "authorize" a specific set of PCR values, which is in essence a digital signature. very demure, very cryptographic ๐
When unsealing the secret during system boot, the boot loader would present the TPM with the original Authorized Policy, and the signed set of PCR values. The TPM then essentially compares the current set of PCR values (as selected by the policy) to the authorized set of values, and verifies the authorization's signature using the public key from the policy. If the PCR values match, and the signature checks out, it will be able to unseal the secret. iykyk ๐
The benefit of this approach is that you can now seal the secret (such as a LUKS password) once โ and when we later update the boot loader, shim loader etc, we just have to authorize the new set of PCR values using the RSA secret key. no re-sealing drama. obsessed. ๐ธ
๐ฆ Policy File Formats ๐ฆ
To make things a little more complex, different file formats are being used to store policies and sealed secrets, depending on the implementation. so yeah. buckle up ๐ข
The current standard is the so-called TPM2.0 key file format, described in https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html. This format combines key material and policies into a single, ASN.1 encoded file. one file to rule them all ๐
In addition, file formats have been used in the past that stored TPM objects in a native format defined in the TPM specification. vintage. retro. arguably iconic.
Finally, the systemd-boot implementation for unlocking disk partitions stores policies in a JSON formatted file, which can be fed to the systemd-cryptenroll tool. This file will contain any number of signed policies that we want systemd-cryptsetup to recognize during boot time. Each entry contains the list of PCRs that compose the PCR policy, a finger print of the public key, the hash of the policy, and the signature (in base64) of the PCR policy. like a little JSON scrapbook of trust ๐ง
pcr-oracle supports all three implementations. The target implementation can be selected using the --target-platform option, which accepts tpm2.0, systemd and oldgrub as arguments, respectively.
The default format is tpm2.0. no cap. โญ
๐ RSA Key File Formats ๐
By default, pcr-oracle stores private RSA keys as PEM files and public RSA keys in native TPM encoded format. This behavior can be tuned, however โ when it detects a file name that has a .pem extension, it will automatically assume that the caller expects the file to be in PEM format. it just knows. ๐ฎ
In addition, you can explicitly force a specific file format by prefixing the entire path by either pem: or native:, respectively. be as explicit as you want, bestie, we respect it ๐
โจ ACTIONS โจ
The pcr-oracle command supports the following actions:
self-testโ Perform a TPM self-test to verify the chip exists and works. like a little vibe check for your silicon ๐rsa-testโ Check whether the TPM supports a given RSA key length. The desired key length is given using the--rsa-bitsoption. that's the whole test. ๐ecc-testโ Check whether the TPM supports ECC SRK keys. short, sweet, elliptic. ๐ฆload-testโ Find out which Storage Root Key a sealed key was created under. The key is given with the--inputor the--nvindexoption. See the SRK detection example below. โฌ๏ธpredictโ Try to predict a specific set of PCR values. the classic. the moment. โจcreate-authorized-policyโ Create a PCR authorized policy. Optionally, a new RSA key can be generated. ๐store-public-keyโ Given a private key, extract the public portion and write it to a separate file. This is useful when a secret key has been generated while creating the authorized policy. split it, ship the public half ๐seal-secretโ Read a secret piece of data from a file, and seal it against a TPM policy (either a PCR policy or an authorized policy). Note that the TPM is limited in the maximum amount of secret data it can seal; it's probably safe to assume a limit of 128 bytes. ๐ฅsignโ When using an authorized policy, predict a set of PCR values and sign them using an RSA key. signature time, periodt. โ๏ธunseal-secretโ This action exists primarily for test purposes. Given a sealed secret and (optionally) a signed policy, unseal the secret and write it to the specified output file. mostly for testing but still lowkey essential. ๐งช
๐ EXAMPLES ๐
This section illustrates the use of pcr-oracle in different scenarios through HOWTO examples. very tutorial. very helpful. we love a walkthrough ๐ซถ
๐ธ TPM Self-test ๐ธ
To perform a TPM self-test, simply execute pcr-oracle like this:
# pcr-oracle self-test
The reason this subcommand exists is that it allows an installer to set up full disk encryption without having to pull in all of tpm-tools. minimal dependency queen ๐
๐ธ TPM RSA key size test ๐ธ
To perform a TPM RSA key size test, simply execute pcr-oracle like this:
# pcr-oracle --rsa-bits 2048 rsa-test
This subcommand allows external programs, such as fde-tools, to find out the largest supported RSA key size. it's giving "asking the TPM politely what it can handle" ๐
๐ธ SRK detection ๐ธ
The parent of a sealed key is chosen when the key is sealed and can never change afterwards; signing a new PCR policy for the key does not touch it. A boot loader that is told to use a different SRK will therefore fail to unseal the key, and the key file itself is of little help: the tpm2key format records a rsaParent boolean, which tells RSA from ECC but not the RSA key size, and the oldgrub format records nothing at all. ๐ yes, really. nothing.
The load-test action answers the question by asking the TPM, since TPM2_Load only succeeds under the actual parent:
# pcr-oracle --input /etc/fde/authorized-policy/sealed.tpm load-test
RSA3072
The algorithm name is written to standard output in the spelling grub2 expects for GRUB_TPM2_SRK_ALG, and the exit status is 0 when a match was found, 1 otherwise. Without further options, the candidates ECC, RSA2048, RSA3072 and RSA4096 are tried in that order; be aware that creating the larger RSA keys takes a while on most TPMs. like, go get a snack. ๐ Passing --ecc-srk or --rsa-bits restricts the test to that single candidate, which turns the action into a check of a configuration you already believe in. confidence, but verified. โจ When the key names a persistent SRK, no probing takes place: the SRK is read from the TPM directly. no guessing needed.
๐ธ Prediction Mode ๐ธ
In prediction mode, pcr-oracle is invoked like this:
# pcr-oracle --from eventlog \
--before --stop-event grub-command=tpm2_key_protector_init \
predict 0,2,4,7,9
This would create a PCR prediction for registers 0, 2, 3, 7, and 9 by scanning the current TPM event log until the point where grub executes a command called tpm2_key_protector_init. ๐ฏ
As it processes the log, it will recompute the hash values for certain events to match the latest changes that were made to the system. For instance, for EFI Boot Variable events, it will consult the current value of a EFI variable and rehash it. The same holds for EFI Boot Service Application events for grub2 or shim; or for EFI GPT events. it's reading the receipts and updating them. iconic. ๐งพ
In the example above, pcr-oracle will print the predicted values to standard output. If you want to process these values with a tool from the tpm2.0-tools package, you may prefer binary output, which you can select by invoking the tool with --format binary. ๐
๐ธ Sealing Against PCR Policies ๐ธ
In order to seal a secret against a predicted set of PCRs, one would typically invoke pcr-oracle like this:
# pcr-oracle --from eventlog \
--target-platform oldgrub \
--before --stop-event grub-command=tpm2_key_protector_init \
--input secret --output sealed \
seal-secret 0,2,4,7,9
Note that the size of the secret to be sealed must not exceed 128 bytes. ๐ that is a hard limit, do not test it, do not push it.
The reverse operation looks like this:
# pcr-oracle --input sealed --output recovered \
--target-platform oldgrub \
unseal-secret 0,2,4,7,9
This will instruct the TPM to unseal the given sealed data, computing the policy hash using the current values of the indicated PCRs. ๐ back it comes.
๐ธ Sealing Against Authorized Policies ๐ธ
The following sequence of commands seals a secret against an authorized policy, and then authorizes a set of PCR values by signing them into a signed policy:
# pcr-oracle \
--target-platform oldgrub \
--private-key policy-key.pem \
--auth authorized.policy \
create-authorized-policy 0,2,4,7,9
# pcr-oracle \
--target-platform oldgrub \
--auth authorized.policy \
--input file-containing-secret \
--output sealed-secret \
seal-secret
# pcr-oracle \
--target-platform oldgrub \
--private-key policy-key.pem \
--from eventlog \
--before --stop-event grub-command=tpm2_key_protector_init \
--output signed.policy \
sign 0,2,4,7,9
three commands, one sealed secret, one authorizing signature, zero drama. the way that pipeline just works ๐ญ ๐
so the first of those commands makes the authorized policy and writes it into a file called authorized.policy ๐ that file, plus the public portion of the RSA key, has to live somewhere your boot path can actually reach when it's time to unlock the LUKS key โ store it somewhere accessible, bestie, no cap.
The second command seals the secret against that policy โจ clear text goes in file-containing-secret, the encrypted result goes in sealed-secret.
And finally, the third command flexes pcr-oracle's prediction logic to predict a specific set of PCR values โ literally exactly the way the prediction mode section above describes ๐ฆ โ then signs those values with the given RSA key and drops the result in signed.policy. Iconic behavior, truly.
Depending on which boot loader you're using, it may help to hand over the public portion of the RSA key in a format that's trivial to handle. Like, adding DER parsing of RSA keys to grub would be so overkill ๐ in a case like that you want the key in a form that can be fed to the TPM chip more or less directly. pcr-oracle supports this via its store-public-key subcommand:
# pcr-oracle \
--private-key policy-key.pem \
--public-key policy-pubkey \
store-public-key
This one reads the RSA private key from the PEM file and writes the public key as a TPM2B_PUBLIC object to the indicated output file policy-pubkey ๐
Alternatively โ if you want that public key stored as a PEM formatted key โ just give the public key file name a .pem extension:
# pcr-oracle \
--private-key policy-key.pem \
--public-key policy-pubkey.pem \
store-public-key
For the deets, please see section RSA Key File Formats ๐
โจ Using the TPM2.0 Policy File Format โจ
The following seals a secret against PCR 0,2,4,7 and writes the result to sealed-pcr.tpm using the new tpm2.0 format ๐ธ
# pcr-oracle \
--target-platform=tpm2.0 \
--input secret \
--output sealed-pcr.tpm \
seal-secret 0,2,4,7
To seal a secret for an authorized policy:
# pcr-oracle \
--target-platform=tpm2.0 \
--auth authorized.policy \
--input secret \
--output sealed-auth.tpm \
seal-secret
To sign the policy against PCR 0,2,4,7 for sealed-auth.tpm:
# pcr-oracle \
--target-platform=tpm2.0 \
--policy-name "sealing test" \
--private-key policy-key.pem \
--input sealed-auth.tpm \
--output sealed-auth-signed.tpm \
sign 0,2,4,7
๐ Sealing and Unsealing using a TPM NV Index ๐
By storing a sealed key directly inside a TPM Non-Volatile (NV) index, bootloaders (such as GRUB) can unseal keys without having to read any key file from disk ๐ that's the whole vibe.
The following command seals a secret against PCR 0,2,4,7 and writes the resulting key container directly into the TPM NV Index 0x01800001:
# pcr-oracle \
--target-platform=tpm2.0 \
--nvindex 0x01800001 \
--input secret \
seal-secret 0,2,4,7
To unseal the secret directly from the TPM NV Index โ no input key file needed at all:
# pcr-oracle \
--target-platform=tpm2.0 \
--nvindex 0x01800001 \
--output recovered \
unseal-secret
It's also possible to update an existing key container with a signed policy and write the resulting container straight to a TPM NV Index ๐ when you do that, the output file on disk is optional, because the key container is stored directly in the NV Index.
For example, to sign a PCR policy for sealed-auth.tpm and write the updated container to the TPM NV Index 0x01800001 without generating/updating a disk file:
# pcr-oracle \
--target-platform=tpm2.0 \
--policy-name "authorized-policy-test" \
--private-key policy-key.pem \
--input sealed-auth.tpm \
--nvindex 0x01800001 \
sign 0,2,4,7
๐ง Generating a Signed Policy for Systemd ๐ง
When you're using systemd-boot instead of grub2, you'd create an authorized policy as above and seal your secret LUKS key against that ๐ซถ the main difference is in the way the signed policy is stored:
# pcr-oracle \
--target-platform=systemd \
--private-key policy-key.pem \
--from eventlog \
--output tpm2-pcr-signature.json \
sign 0,2,4,7,9,12
This predicts a set of PCR values, signs them using the specified private key, and then stores the signature in tpm2-pcr-signature.json โจ heads up: this command will not overwrite the JSON file โ it updates it by adding a new entry. If there already is an entry for the predicted set of PCR values, this merely updates the public key fingerprint and signature. Lowkey polite of it, honestly.
These policies can then be enrolled for future system boots using systemd-cryptenroll:
# systemd-cryptenroll \
--tpm2-device=auto \
--tpm2-public-key=public-key.pem \
--tpm2-public-key-pcrs="0,2,4,7,9,12" --tpm2-signature=tpm2-pcr-signature.json \
/dev/sda3
๐ Creating and Replaying Test Cases ๐
Firmwares tend to be buggy โ like every piece of code, tbh โ and UEFI firmwares are no different ๐ญ in order to create a broad base of test cases to validate pcr-oracle against, it comes with support for creating and replaying test cases.
A test case is mostly a dump of the data that pcr-oracle processes when re-hashing the events from the TPM event log ๐ this includes the content of UEFI variables such as Boot entries, variables related to Secure Boot, GPT headers, full copies of any EFI Applications loaded during bootup (such as shim.efi, grub.efi), and hashes for other files loaded by the boot loader (such as grub.cfg, or the kernel and initrd). It's giving forensic scrapbook, and we love that for us.
The following command creates a test case and stores it in a directory tree at /tmp/pcr-oracle.test:
# pcr-oracle --from eventlog --verify current -d \
--create-testcase /tmp/pcr-oracle.test \
predict all
If you want to submit your test case, please use tar to archive the test case and create an issue in the github issue tracker at github.com/okirch/pcr-oracle ๐ฆ
To replay a test case, you can do this:
# pcr-oracle --from eventlog --verify current -d \
--replay-testcase /tmp/pcr-oracle.test \
predict all
โจ OPTIONS โจ
okay bestie, this is the big one โ every single flag, no skipping, we're going through all of them ๐
--from sourceโ in prediction mode, this tells pcr-oracle where to start the PCRs from. Your choices areeventlog,current, orzero. ๐
with eventlog, the tool scans the TPM event log and re-computes the event hashes based on the current state of the system, fr fr.
with zero, prediction starts with all PCRs cleared. with current, prediction starts with all PCRs set to the current values of the system's TPM. pick your vibe ๐ฆ
-Z,--from-zeroโ the shorthand bestie: sets the PCR initial state to zero. literally the same as--from zero.-C,--from-currentโ shorthand that sets the PCR initial state to the current state of the host's PCRs. equivalent to--from currentโจ-S,--from-snapshotโ shorthand that sets the PCR initial state to a snapshot taken during boot. equivalent to--from snapshot๐-L,--from-eventlogโ shorthand that sets the PCR initial state predicted from the event log. equivalent to--from eventlog.--algorithm hash-algโ by default pcr-oracle operates on PCR registers using thesha256hash algorithm. this option lets you pick a different one, assuming the chip supports it. ngl, for backward compatibility with version 1 of the specification, all TPMv2 chips also supportsha1โ but using that is not recommended ๐--verify sourceโ after applying all the updates, compare the predicted PCR values against the given source (e.g.currentorsnapshot). vibe check for your PCRs ๐--format fmtโ in prediction mode, pcr-oracle writes predicted PCR values to standard output in a human readable format. this option picks a different output format. ๐ธ
a format of binary writes the predicted values as a binary octet stream, all values concatenated together. this format can be used with tools like tpm2_policypcr(1). a format of tpm2-tools will use a reporting format โ similar to the one that the tpm2-tools produce as output. the default behavior is selected by format plain.
--stop-event event-descโ specify at which point to stop processing the TPM event log, and report the predicted PCR values. the format of event-desc is an event type, followed by a=, followed by a string argument.
currently only two event types are supported: grub-command and grub-file. these correspond to the IPL event emitted by grub when executing a command, or reading a file, respectively.
for a grub command event, the string argument is the name of the command, with no arguments. for a grub file event, the string argument is a file name or a (partial) file path. path tail matching is performed, i.e. an event for reading EFI/BOOT/grub.cfg can be matched by specifying a stop event for grub.cfg, BOOT/grub.cfg, or EFI/BOOT/grub.cfg, respectively. โจ
--beforeโ when a stop event has been given, report predicted PCR values before processing the event. this is the default behavior ๐--afterโ when a stop event has been given, report predicted PCR values after processing the event. this may be useful, for example, if you don't measure grub's command events (tracked in PCR8) but only its file load events (tracked in PCR9). in that case it would be totally sufficient to stop processing after grub loadedgrub.cfgfrom the EFI System Partition. lowkey genius ๐ง--disable-synthesisโ disables predictive event synthesis and deduplication for shim extra files. periodt.--boot-entry idโ in a systemd-boot environment, the values of PCR registers also depend on the contents of the kernel and initrd image, as well as the kernel command line options. after a kernel update or an initrd rebuild, these values will change ๐ญ so when predicting PCR values in this case, pcr-oracle needs to be told to refer to the future kernel rather than the current one.
that's what --boot-entry is for. it takes one argument, which is either an ID (in the sense of systemd-boot IDs) or auto. with auto, pcr-oracle makes a best guess as to what kernel image will be used on next boot. ๐ฆ
--next-kernel nameโ an alias for--boot-entry. specifying this sets the boot entry to use for the next boot prediction.--auth path,--authorized-policy pathโ specify the location of the authorized policy. together with thecreate-authorized-policyaction, the newly created policy is written to this location. for subsequent actions, such asseal, the policy is read from this location. ๐--pcr-policy pathโ specify the location of the PCR policy file used during authorized policy unsealing.--input pathโ specify the input secret file path for sealing, or a sealed secret for unsealing.--output pathโ specify the output destination path for sealing, unsealing, or public key storage.--private-key pathโ specify the RSA secret key to be used with authorized policies. for notes on the file format, please see section RSA Key File Formats. ๐--public-key pathโ specify an RSA public key to be used with authorized policies. for notes on the file format, please see section RSA Key File Formats. ๐--rsa-generate-keyโ when used while creating an authorized policy, a signature, or when storing the public key, this generates the RSA private key "on the fly". this should not be different from whatopenssl genrsadoes โ the only reason this switch exists is that it may save you from increasing the footprint of your installed system (by not having to install theopensslcommand line utilities, for example). iconic, honestly โจ--rsa-bits bitsโ by defaultRSA 2048is used as the algorithm to create the public and private key and configure the storage root key (SRK) in TPM. this option lets the user specify the larger RSA key size. the supported key sizes are:2048,3072, and4096bits. ๐--ecc-srkโ switches the storage root key (SRK) configuration to Elliptic Curve Cryptography (ECC P-256) instead of the default RSA algorithm. slay ๐--persistent-srk handle/indexโ configure a persistent storage root key (SRK) handle or index on the TPM.-n,--nvindex handleโ specify the TPM NV Index handle (which must start with0x01in the MSB, e.g.0x01800001) to write, sign, or unseal a key container. this option is currently only supported when using thetpm2.0target platform. when sealing or signing a policy, the resulting key container is stored directly in the specified NV space (even if no disk-based output file is specified). when unsealing, the key container is read directly from the specified NV space, bypassing the need for a disk-based input file. ๐--tpm-eventlog pathโ by default the tool reads the current TPM event log. it's possible to process an event log generated on a different system by specifying it with this option. ๐ฆ--target-platform nameโ write key and policy information using file format(s) compatible with the specified target implementation. please see the section Policy File Formats for more information.--key-format nameโ deprecated alias for--target-platform. prints a warning to use--target-platforminstead. โ ๏ธ--policy-format nameโ deprecated alias for--target-platform. prints a warning to use--target-platforminstead. โ ๏ธ--policy-name nameโ for thetpm2.0format there's an extra field,Name, for the authorized policies. this option is only valid when signing the sealed key intpm2.0format. theNameis optional and only used for display purposes. if the user doesn't specify a name, the default name isdefault. ๐--create-testcase pathโ dumps current boot state variables, files, and hashes into a directory tree at the given path, to create a test case for regression testing. ๐งช--replay-testcase pathโ replays a previously captured test case from the directory tree at path, simulating the recorded system state. ๐--compare-currentโ complements--replay-testcaseto compare predictions of the test case against the host's actual current event log. ๐
๐ SEE ALSO ๐
tpm2_policypcr(1), tpm2_policyauthorize(1), tpm2_create(1).
go read those too bestie, iykyk ๐ซถ
๐ AUTHOR ๐
pcr-oracle was written by Olaf Kirch, with significant contributions from Gary Lin, Michael Chang and Alberto Planas. legends, every single one ๐ญ๐
and that's the whole page, no cap ๐
this is version 0.6.6, dated September 18, 2026 โ the PCR-ORACLE(8) manual, and honestly? it ate. go predict some PCRs, bestie, you've got this ๐โจ๐
๐ฆ where this page comes from
- opensuse-leap 16.0: pcr-oracle 0.6.6-160000.1.1 ยท GPL-2.0-or-later
- opensuse-leap 16.0: pcr-oracle 0.6.6-160000.1.1 ยท GPL-2.0-or-later
- opensuse-leap 16.0: pcr-oracle 0.6.6-160000.1.1 ยท GPL-2.0-or-later
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms