Keylime Documentation

Warning

This documentation is still under development and not complete. It will be so until this warning is removed.

Welcome to the Keylime Documentation site!

Keylime is a TPM-based highly scalable remote boot attestation and runtime integrity measurement solution. Keylime enables cloud users to monitor remote nodes using a hardware based cryptographic root of trust.

Keylime was originally born out of the security research team in MIT’s Lincoln Laboratory and is now developed and maintained by the Keylime community.

This Documentation site contains guides to install, use and administer keylime as well as guides to enable developers to make contributions to keylime or develop services against Keylime’s Rest API(s).

Installation

There are three current methods for installing Keylime: the Ansible role, the Keylime installer or a manual installation.

Ansible Keylime Roles

An Ansible role to deploy Keylime , alongside the Keylime Rust cloud agent

Warning

Please note that the Rust cloud agent is still under early stages of development. Those wishing to test drive Keylimes functionality should use the existing Python based cloud agent keylime_agent until later notice.

This role deploys Keylime for use with a Hardware TPM.

Should you wish to deploy Keylime with a software TPM emulator for development or getting your feet wet, use the Ansible Keylime Soft TPM role instead.

Usage

Download or clone Ansible Keylime from its repository and follow the usage section.

Run the example playbook against your target remote host(s):

ansible-playbook -i your_hosts playbook.yml

TPM Version Control (Software TPM)

Ansible Keylime Soft TPM provides a role type for 2.0 TPM versions.

TPM 2.0 support can be configured by simply adding the role in the playbook.yml file here

For TPM 2.0 use:

- ansible-keylime-tpm20

This rule uses the TPM 2.0 Emulator (IBM software TPM).

Vagrant

If you prefer, a Vagrantfile is available for provisioning.

Clone the repository and then simply run:

vagrant up --provider <provider> --provision

For example, using libvirt:

vagrant up --provider libvirt --provision

For example, using VirtualBox:

vagrant up --provider virtualbox --provision

Once the VM is started, vagrant ssh into the VM and run sudo su - to become root.

You can then start the various components using commands:

keylime_verifier
keylime_registrar
keylime_agent

Rust Cloud agent

To start the rust cloud agent, navigate to it’s repository directory and use cargo to run:

[root@localhost rust-keylime]# RUST_LOG=keylime_agent=trace cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.28s
    Running `target/debug/keylime_agent`
    INFO  keylime_agent > Starting server...
    INFO  keylime_agent > Listening on http://127.0.0.1:1337

Keylime Bash installer

Keylime requires Python 3.7 for dataclasses support.

Installation can be performed via an automated shell script, installer.sh. The following command line options are available:

Usage: ./installer.sh [option...]
Options:
-k              Download Keylime (stub installer mode)
-t              Create tarball with keylime_agent
-m              Use modern TPM 2.0 libraries (vs. TPM 1.2)
-s              Install TPM in socket/simulator mode (vs. chardev)
-p PATH         Use PATH as Keylime path
-h              This help info

Docker - Deployment

The verifier, registrar and tenant can also be deployed using Docker images. Keylime’s offical images can be found here. Those are automatically generated for every commit and release.

For building those images locally see here.

Docker - Development

Python Keylime and with a TPM emulator can also be deployed using Docker. Since this docker configuration uses a TPM emulator, it should only be used for development or testing and NOT in production.

Please see either the Dockerfiles here or our local CI script here which will automate the build and pull of Keylime.

Manual

Keylime requires Python 3.7 or newer to work properly out of the box because older versions do not support dataclasses.

Python-based prerequisites

The following Python packages are required:

  • cryptography>=3.3.2

  • tornado>=5.0.2

  • m2crypto>=0.21.1

  • pyzmq>=14.4

  • pyyaml>=3.11

  • simplejson>=3.8

  • requests>=2.6

  • sqlalchemy>=1.3

  • alembic>=1.1.0

  • python-gnupg>=0.4.6

  • packaging>=16.0

  • psutil>=5.4.2

The current list of required packages can be found here.

All of them should be available as distro packages. See installer.sh for more information if you want to install them this way. You can also let Keylime’s setup.py install them via PyPI.

TPM 2.0 Support

Keylime uses the Intel TPM2 software set to provide TPM 2.0 support. You will need to install the tpm2-tss software stack (available here) and tpm2-tools utilities available here. See README.md in these projects for detailed instructions on how to build and install.

The brief synopsis of a quick build/install (after installing dependencies) is:

# tpm2-tss
git clone https://github.com/tpm2-software/tpm2-tss.git tpm2-tss
pushd tpm2-tss
./bootstrap
./configure --prefix=/usr
make
sudo make install
popd
# tpm2-tools
git clone https://github.com/tpm2-software/tpm2-tools.git tpm2-tools
pushd tpm2-tools
./bootstrap
./configure --prefix=/usr/local
make
sudo make install

To ensure that you have the recent version installed ensure that you have the tpm2_checkquote utility in your path.

Note

Keylime by default (all versions after 6.2.0) uses the kernel TPM resource manager. For kernel versions older than 4.12 we recommend to use the tpm2-abrmd resource manager (available here).

How the TPM is accessed by tpm2-tools can be set using the TPM2TOOLS_TCTI environment variable. More information about that can be found here.

Talk to the swtpm emulator directly:

export TPM2TOOLS_TCTI="mssim:port=2321"

To talk to the TPM directly (not recommended):

export TPM2TOOLS_TCTI="device:/dev/tpm0"

Install Keylime

You’re finally ready to install Keylime:

sudo python setup.py install

Database support

Keylime supports the following databases:

* SQLite
* PostgreSQL
* MySQL
* Oracle
* Microsoft SQL Server

SQLite is supported as default.

Each database is configured within /etc/keylime.conf for both the keylime_verifier and keylime_registrar databases.

SQLite

The following illustrates examples for SQLite and PostgreSQL:

database_drivername = sqlite
database_username = ''
database_password = ''
database_host = ''
database_port = ''
database_name = cv_data.sqlite
database_query = ''

PostgreSQL

For PostgreSQL you will need to install the database first and set up a user account:

database_drivername = postgresql
database_username = keylime
database_password = allyourbase
database_host = localhost
database_port = 5432
database_name = keylime_db
database_query = ''

For details on other platforms, please refer to the SQLAlchemy documentation on engine configuration.

User Guide

User Selected PCR Monitoring

Warning

This page is still under development and not complete. It will be so until this warning is removed.

Using use the tpm_policy feature in Keylime, it is possible to monitor a remote machine for any given PCR.

This can be used for Trusted Boot checks for both the rhboot shim loader and Trusted Grub 2.

Note

On larger deployments the PCR values might be insufficient. In this case use the UEFI event log for measured boot: Use Measured Boot.

How to use

Select which PCRs you would like Keylime to measure, by using the tpm2_pcrread from the tpm2-tools tool.

Now you can set the PCR values as a JSON data structure in either the keylime.conf file:

tpm_policy = {"22":["0000000000000000000000000000000000000001","0000000000000000000000000000000000000000000000000000000000000001","000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001","ffffffffffffffffffffffffffffffffffffffff","ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"],"15":["0000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"]}

Or you can add a node to using keylime_tenant:

keylime_tenant -v 127.0.0.1 -t 127.0.0.1 -f /root/excludes.txt \
--uuid D432FBB3-D2F1-4A97-9EF7-75BD81C00000 \
--allowlist /root/allowlist.txt \
--exclude /root/exclude.txt \
--tpm_policy  {"22":["0000000000000000000000000000000000000001","0000000000000000000000000000000000000000000000000000000000000001","000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001","ffffffffffffffffffffffffffffffffffffffff","ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"],"15":["0000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"]} \
-c add

rhboot shim-loader

The following is sourced from the rhboot shim repository please visit the upstream README to ensure information is still accurate

The following PCRs are extended by shim:

PCR4:
  • the Authenticode hash of the binary being loaded will be extended into PCR4 before SB verification.

  • the hash of any binary for which Verify is called through the shim_lock protocol

PCR7:
  • Any certificate in one of our certificate databases that matches a binary we try to load will be extended into PCR7. That includes:

    • DBX - the system denylist, logged as “dbx”

    • MokListX - the Mok denylist, logged as “MokListX”

    • vendor_dbx - shim’s built-in vendor denylist, logged as “dbx”

    • DB - the system allowlist, logged as “db”

    • MokList the Mok allowlist, logged as “MokList”

    • vendor_cert - shim’s built-in vendor allowlist, logged as “Shim”

    • shim_cert - shim’s build-time generated allowlist, logged as “Shim”

  • MokSBState will be extended into PCR7 if it is set, logged as “MokSBState”.

PCR8:
  • If you’re using the grub2 TPM patchset we cary in Fedora, the kernel command line and all grub commands (including all of grub.cfg that gets run) are measured into PCR8.

PCR9:
  • If you’re using the grub2 TPM patchset we cary in Fedora, the kernel, initramfs, and any multiboot modules loaded are measured into PCR9.

PCR14:
  • MokList, MokListX, and MokSBState will be extended into PCR14 if they are set.

Use Measured Boot

Warning

This page is still under development and not complete. It will be so until this warning is removed.

Introduction

In any real-world large-scale production environment, a large number of different types of nodes will typically be found. The TPM 2.0 defines specific meaning - measurement of UEFI bios, measurement of boot device firmware - for lower-numbered PCRs (e.g., PCRs 0-9), as these are extended during the multiple events of a measured boot log. However, simply comparing the contents of these PCRs against a well-known “golden value” becomes unfeasible. The reason for this is, in addition to the potentially hundreds of variations due to node type, that it can be experimentally demonstrated that some PCRs (e.g, PCR 1) vary for each physical machine, if such machine is netbooted (as it encodes the MAC address of the NIC used during boot.)

Fortunately, the UEFI firmware is now exposing the event log through an ACPI table and a “recent enough” Linux kernel (e.g., 5.4 or later) is now consuming this table and exposing this boot event log through the securityfs, typically at the path /sys/kernel/security/tpm0/binary_bios_measurements. When combined with secure boot and a “recent enough” version of grub (TBD), the aforementioned can be fully populated, including measurements of all components, up to the kernel and initrd.

In addition to these sources of (boot log) data, a “recent enough” version of tpm2-tools (5.0 or later) can be used to consume the contents of such logs and thus rebuild the contents of PCRs [0-9] (and potentially PCRs [11-14]).

Implementation

Keylime can make use of this new capability in a very flexible manner. A “measured boot reference state” or mb_refstate for short can be specified by the keylime operator (i.e. the tenant). This operator-provided piece of information is used, in a fashion similar to the “IMA policy” (previously known as “allowlist”), by the keylime_verifier, to compare the contents of the information shipped from the keylime_agent (boot log in one case, IMA log on the other), against such reference state.

Due to the fact that physical node-specific information can be encoded on the “measured boot log”, it became necessary to specify (optionally) a second piece of information, a “measured boot policy”. This information is used to instruct the keylime_verifier on how to do the comparison (e.g., using a regular expression, rather than a simple equality match). The policy name is specified in keylime.conf, under the [cloud_verifier] section of the file, with paramater named measured_boot_policy_name. The default value for it is accept-all, meaning “just don’t try to match the contents”.

Whenever a “measured boot reference state” is defined - via a new command-line option in keylime_tenant - –mb_refstaste, the following actions will be taken.

  1. PCRs [0-9] and [11-14] will be included in the quote sent by keylime_agent

2) The keylime_agent will also send the contents of /sys/kernel/security/tpm0/binary_bios_measurements

3) The keylime_verifier will replay the boot log from step 2, ensuring the correct values for PCRs collected in step 1. Again, this is very similar to what it is done with “IMA logs” and PCR 10.

4) The very same keylime_verifier will take the boot log, now deemed “attested” and compare it against the “measured boot reference state”, according to the “measured boot policy”, causing the attestation to fail if it does not conform.

How to use

The simplest way to use this new functionality is by providing an empty “measured boot reference state” and an accept-all “measured boot policy”, which will cause the keylime_verifer to simply skip the aforementioned step 4.

An example follows:

echo "{}" > measured_boot_reference_state.txt

keylime_tenant -c add -t <AGENT IP> -v <VERIFIER IP> -u <AGENT UUID> --mb_refstate ./measured_boot_reference_state.txt

Note: please keep in mind that the IMA-specific options can be combined with the above options in the example, resulting in a configuration where a keylime_agent sent a quote with PCRs [0-15] and both logs (boot and IMA)

Run-time Integrity Monitoring

Keylimes run-time integrity monitoring requires the set up of Linux IMA.

You should refer to your Linux Distributions documentation to enable IMA, but as a general guide most recent versions already have CONFIG_IMA toggled to Y as a value during Kernel compile.

It is then just a case of deploying an ima-policy file. On a Fedora or Debian system, the file is situated in /etc/ima/ima-policy.

For configuration of your IMA policy, please refer to the IMA Documentation

Within Keylime we use the following for demonstration:

# PROC_SUPER_MAGIC
dont_measure fsmagic=0x9fa0
# SYSFS_MAGIC
dont_measure fsmagic=0x62656572
# DEBUGFS_MAGIC
dont_measure fsmagic=0x64626720
# TMPFS_MAGIC
dont_measure fsmagic=0x01021994
# RAMFS_MAGIC
dont_measure fsmagic=0x858458f6
# SECURITYFS_MAGIC
dont_measure fsmagic=0x73636673
# MEASUREMENTS
measure func=BPRM_CHECK
measure func=FILE_MMAP mask=MAY_EXEC
measure func=MODULE_CHECK uid=0

This default policy measures all executables in bprm_check, all files mmapped executable in file_mmap and module checks.

Once your ima-policy is in place, reboot your machine (or even better have it present in your image for first boot).

You can then verify IMA is measuring your system:

# head -5 /sys/kernel/security/ima/ascii_runtime_measurements
PCR                                  template-hash filedata-hash                                 filename-hint
10 3c93cea361cd6892bc8b9e3458e22ce60ef2e632 ima-ng sha1:ac7dd11bf0e3bec9a7eb2c01e495072962fb9dfa boot_aggregate
10 3d1452eb1fcbe51ad137f3fc21d3cf4a7c2e625b ima-ng sha1:a212d835ca43d7deedd4ee806898e77eab53dafa /usr/lib/systemd/systemd
10 e213099a2bf6d88333446c5da617e327696f9eb4 ima-ng sha1:6da34b1b7d2ca0d5ca19e68119c262556a15171d /usr/lib64/ld-2.28.so
10 7efd8e2a3da367f2de74b26b84f20b37c692b9f9 ima-ng sha1:af78ea0b455f654e9237e2086971f367b6bebc5f /usr/lib/systemd/libsystemd-shared-239.so
10 784fbf69b54c99d4ae82c0be5fca365a8272414e ima-ng sha1:b0c601bf82d32ff9afa34bccbb7e8f052c48d64e /etc/ld.so.cache

Keylime IMA allowlists

An allowlist is a set of “golden” cryptographic hashes of a files un-tampered state or of keys that may be loaded onto keyrings.

The structure of the allowlist is a hash followed by a full POSIX path to the file:

ffe3ad4c395985d143bd0e45a9a1dd09aac21b91 /path/to/file

For a key that is expected to be loaded on a keyring with the name .ima an entry may look like this:

b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c %keyring:.ima

Keylime will load the allowlist into the Keylime Verifier. Keylime will then poll tpm quotes to PCR 10 on the agents TPM and validate the agents file(s) state against the allowlist. If the object has been tampered with or an unexpected key was loaded onto a keyring, the hashes will not match and Keylime will place the agent into a failed state. Likewise, if any files invoke the actions stated in ima-policy that are not matched in the allowlist, keylime will place the agent into a failed state.

Generate an allowlist

Keylime provides a script to generate allowlists from initramfs, but this is only a guide. We encourage developers / users of Keylime to be creative and come up with their own process for securely creating and maintaining an allowlist.

The create_allowlist.sh script is available here

Run the script as follows:

# create_allowlist.sh  allowlist.txt [hash-algo]

With [hash-algo] being sha1sum, sha256sum (note, you need the OpenSSL app installed to have the shasum CLI applications available).

This will then result in allowlist.txt being available for Agent provisioning.

Warning

It’s best practice to create the allowlist in a secure environment. Ideally, this should be on a fully encrypted, air gapped computer that is permanently isolated from the Internet. Disable all network cards and sign the allowlist hash to ensure no tampering occurs when transferring to other machines.

Alongside building an allowlist from initramfs, you could also generate good hashes for your applications files or admin scripts that will run on the remotely attested machine.

Excludes List

An excludes list can be utilised to exclude any file or path. The excludes list uses the Python regular expression standard, where the syntax is similar to those found in Perl. Note that this syntax is different from POSIX basic regular expressions. For example the tmp directory can be ignored using:

/tmp/.*
Allowlist entries for keys

Allowlist entries for keys expected to be loaded onto keyrings can be generated by hashing the files of keys like this:

sha256sum /etc/keys/ima/rsakey-rsa.crt.der

As previously shown, the allowlist entry should be formed of the hash (sha256) and the prefix ‘%keyring:’ in front of the keyring the key will be loaded onto:

b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c %keyring:.ima

The following rule should be added to the IMA policy so that IMA reports keys loaded onto keyrings .ima and .evm (since Linux 5.6):

measure func=KEY_CHECK keyrings=.ima|.evm
IMA Keylime JSON format

The tenant parses the allow and exclude list into a JSON object that is then sent to the verifier. Depending of the use case the object can also be constructed manually instead of using the tenant.

{
   "allowlist":{
      "meta":{
         "version":"ALLOWLIST_CURRENT_VERSION"
      },
      "release":"RELEASE_VERSION",
      "hashes":{
         "/file/path":[
            "VALID_HASH1",
            "VALID_HASH2"
         ]
      },
      "keyrings":{
         "LINUX_KEYRING":[
            "VALID_HASH3"
         ]
      },
      "ima":{
         "ignored_keyrings":[
            "IGNORED_KEYRING"
         ]
      }
   },
   "exclude":[
      "REGEX1, REGEX2"
   ]
}
  • ALLOWLIST_CURRENT_VERSION (integer): current version of the allow list format (latest is 2).

  • RELEASE_VERSION (integer): release version of this allowlist.

  • hashes: dictionary of the file path that should be validated as key and a list of valid hashes as entry.

  • VALID_HASHn: valid hash of the file or keyring that is measured

  • keyrings: dictionary of the keyring that should be used for signature validation and a list of valid hashes as entry.

  • LINUX_KEYRING: kernel keyring like .ima or .evm

  • ignored_keyrings: successful validated keyrings are used for signature validation. Add * to disable all or add them one by one.

  • exclude: list of regexes of files to exclude

  • REGEXn: regex for excluding certain files (e.g. /tmp/.*)

Remotely Provision Agents

Now that we have our allowlist available, we can send it to the verifier.

Note

If you’re using a TPM Emulator (for example with the ansible-keylime-tpm-emulator, you will also need to run the keylime ima emulator. To do this, open a terminal and run keylime_ima_emulator

Using the keylime_tenant we can send the allowlist and our excludes list as follows:

keylime_tenant -v <verifier-ip> -t <agent-ip> -f /path/excludes.txt --uuid D432FBB3-D2F1-4A97-9EF7-75BD81C00000 --allowlist /path/allowlist.txt --exclude /path/excludes.txt

Note

If your agent is already registered, you can use -c update

Should you prefer, you can set the values allowlist & ima_excludelist within /etc/keylime.conf, you can then use default as follows:

`keylime_tenant -v 127.0.0.1 -t neptune -f /root/excludes.txt --uuid D432FBB3-D2F1-4A97-9EF7-75BD81C00000 --allowlist default --exclude default`

How can I test this?

Create a script that does anything (for example echo “hello world”) that is not present in your allowlist or the excludes list. Run the script as root on the agent machine. You will then see the following output on the verifier showing the agent status change to failed:

keylime.tpm - INFO - Checking IMA measurement list...
keylime.ima - WARNING - File not found in allowlist: /root/evil_script.sh
keylime.ima - ERROR - IMA ERRORS: template-hash 0 fnf 1 hash 0 good 781
keylime.cloudverifier - WARNING - agent D432FBB3-D2F1-4A97-9EF7-75BD81C00000 failed, stopping polling

IMA File Signature Verification

Keylime supports the verification of IMA file signatures, which also helps to detect modifications on immutable files and can be used to complement or even replace the allowlist of hashes if all relevant executables and libraries are signed. However, the set up of a system that has all files signed is beyond the scope of this documentation.

In the following we will show how files can be signed and how a system with signed files must be registered. We assume that the system has already been set up for runtime-integrity monitoring following the above steps and that the system would not show any errors on the Keylime Verifier side. It should not be registered with the keylime verifier at this point. If it is, we now deregister it:

keylime_tenant -c delete -u D432FBB3-D2F1-4A97-9EF7-75BD81C00000

Our first step is to enable IMA Appraisal in Linux. Recent Fedora kernels for example have IMA Appraisal support built-in but not activated. To enable it, we need to add the following Linux kernel parameters to the Linux boot command line:

ima_appraise=fix ima_template=ima-sig ima_policy=tcb

For this we edit /etc/default/grub and append the above parameters to the GRUB_CMDLINE_LINUX line and then recreate the system’s grub configuration file with the following command:

sudo grub2-mkconfig -o /boot/grub2/grub.cfg

IMA will be in IMA Appraisal fix-mode when the system is started up the next time. Fix-mode, unlike enforcement mode, does not require that all files be signed but will give us the benefit that the verifier receives all file signatures of signed executables.

For IMA Appraisal to append the file signatures to the IMA log, we need to append the following line to the above IMA policy:

appraise func=BPRM_CHECK fowner=0 appraise_type=imasig

We now create our IMA file signing key using the following commands:

openssl genrsa -out ima-filesigning.pem 2048
openssl rsa -in ima-filesigning.pem -pubout -out ima-pub.pem

Next, we determine the hash (sha1 or sha256) that IMA is using for file measurements by looking at the IMA measurement log and then use evmctl to sign a demo executable that we derive from the echo tool:

sudo dnf -y install ima-evm-utils
cp /bin/echo ./myecho
sudo evmctl ima_sign --key ima-filesigning.pem -a <hash> myecho

Note

It is important that we use the same hash for signing the file that IMA also uses for file measurements. In the case we use ‘sha1’ since the IMA measurement log further above shows sha1 filedata-hashes in the 4th column. On more recent systems we would likely use ‘sha256’.

Note

If the IMA measurement log contains invalid signatures, the system will have to be rebooted to start over with a clean log that the Keylime Verifier can successfully verify.

Invalid signatures may for example be in the log if executables were accidentally signed with the wrong hash, such as sha1 instead of sha256. In this case they all need to be re-signed to match the hash that IMA is using for file signatures.

Another reason for an invalid signature may be that a file was modified after it was signed. Any file modification will invalidate the signature. Similarly, a malformatted or altered security.ima extended attribute will lead to a signature verification failure.

Yet another reason may be that an unknown key was used for signing files. In this case the system should be re-registered with that additional key using the Keylime tenant tool.

To verify that the file has been properly signed, we can use the following command, which will show the security.ima extended attribute’s value:

getfattr -m ^security.ima --dump myecho

We now reboot the machine:

reboot

After the reboot the IMA measurement log should not have any measurement of the myecho tool. The following command should not return anything:

grep myecho /sys/kernel/security/ima/ascii_runtime_measurements

We now register the system and pass along the file signing key:

keylime_tenant -v 127.0.0.1 -t neptune -f /root/excludes.txt \
  --uuid D432FBB3-D2F1-4A97-9EF7-75BD81C00000 --allowlist default --exclude default \
  --sign_verification_key ima-pub.pem

We can now execute the myecho tool as root:

sudo ./myecho

At this point we should not see any errors on the verifier side and there should be one entry of ‘myecho’ in the IMA measurement log that contains a column after the file path containing the file signature:

grep myecho /sys/kernel/security/ima/ascii_runtime_measurements

To test that signature verification works, we can now invalidate the signature by appending a byte to the file and executing it again:

echo >> ./myecho
sudo ./myecho

We should now see two entries in the IMA measurement log. Each one should have a different measurement:

grep myecho /sys/kernel/security/ima/ascii_runtime_measurements

The verifier log should now indicating a bad file signature:

keylime.tpm - INFO - Checking IMA measurement list on agent: D432FBB3-D2F1-4A97-9EF7-75BD81C00000
keylime.ima - WARNING - signature for file /home/test/myecho is not valid
keylime.ima - ERROR - IMA ERRORS: template-hash 0 fnf 0 hash 0 bad-sig 1 good 3042
keylime.cloudverifier - WARNING - agent D432FBB3-D2F1-4A97-9EF7-75BD81C00000 failed, stopping polling

Secure Payloads

Warning

This page is still under development and not complete. It will be so until this warning is removed.

Secure payloads offer the ability to provision encrypted data to an enrolled node. This encrypted data can be used to deliver secrets needed by the node such as keys, passwords, certificate roots of trust, etc.

Secure payloads are for anything which requires strong confidentiality and integrity to bootstrap your system.

The payload itself is encrypted and sent via the Keylime Tenant CLI (or rest API) to the Keylime Agent. The Agent also sends part of the key needed to decrypt the payload, a key share, called the u_key or user key. Only when the Agent has passed its enrolment criteria (including any tpm_policy or IMA allowlist), will the other key share of the decryption key, called the v_key or verification key, be passed to the Agent by the Keylime Verifier to decrypt the payload.

Note

An alternative to secure payloads is to deliver the encrypted data to the node through some other mechanism like cloud-init or pre-embedded in a disk image. The Keylime protocol described above will still run to derive the decryption key for this data, but the data itself will never been seen or transported by Keylime. This guide does not discuss this method.

Keylime offers two modes for sending secure payloads: single file encryption and certificate package mode. In the following sections we describe each. If you’re interested in using the more advanced certificate package mode, we recommend you also read the Single File Encryption section as it contains configuration options and other information that both modes share.

Single File Encryption

In this mode, a file you specify to the keylime_tenant application with the -f option will be encrypted by the Tenant using the bootstrap key and securely delivered to the Agent. Once the Keylime protocol with the Tenant and Verifier has completed, the Keylime Agent will decrypt this file and place it in /var/lib/keylime/secure/decrypted_payload This is the default file name, but you can adjust the name of this file using the dec_payload_file option in keylime.conf. You can also optionally specify a zip file as the file to be securely delivered. If the extract_payload_zip option in keylime.conf is set (which it is by default), then Keylime will automatically extract the zip file to /var/lib/keylime/secure/unzipped. Finally, Keylime can also execute a script contained in the zip file once it has been unzipped. You can think of this as a very simple form of cloud-init. By default this script is called autorun.sh. You can override this default with a different script name by adjusting the payload_script option in keylime.conf. Note also that this script must be contained in the encrypted zip file, from which it will be extraced and then placed in /var/lib/keylime/secure/unzipped.

Because the keys that Keylime uses to decrypt the data and the decrypted data itself are very sensitive, Keylime will only write those files to the memory-backed (and therefore non-persistent) /var/lib/keylime/secure directory. This is a bind-mounted tmpfs partition. As such, depending on how large your payload is, you may need to increase the size of this mounted partition by adjusting the secure_size option in keylime.conf.

This simple mode of operation is suitable for many situations where the secrets and other bootstrapping information are basic. However, there are several features that Keylime supports like revocation and certificate management that do not work in this mode. For those, you’ll need the next mode: Certificate Package Mode.

Certificate Package Mode

This mode of Keylime automates many common actions that tenants will want to take when provisioning their Agents. First, Keylime can create an X509 certificate authority (CA) using keylime_ca -d listen and then issue certificates and the corresponding private keys to each provisioned node. This CA lives on the same host where the tenant issues the keylime_ca command and can be used to bootstrap many other security solutions like mutual TLS or SSH. To use this mode, pass the –cert option and a directory where the CA is located as the parameter to this option. Keylime will then create a certificate for the node (with the common name set to the Agent’s UUID) and then create a zip file containing the newly generated X509 certificates, trust roots, and private keys. It then uses the same process for single file encryption as described above to securely deliver all the keys to the Agent. Optionally, the user can specify with the –include option a directory of additional files to be put into the certification package zip and securely delivered to the Agent.

This mode of operation also natively supports certificate revocation. If the Keylime Verifier detects an Agent that no longer satisfies its integrity policy (e.g., it booted an authorized kernel or ran an unauthorized binary not on the IMA allowlist), it will create a signed revocation notification. These revocation notifications are signed by a special certificate/private key called the RevocationNotifier. Keylime will automatically create this certificate and pass it to the verifier when you add a new Agent to the verifier. Keylime will also include the public certificate for this key in the zip it sends to the Agent. This way Agents can validate the revocation notifications they receive from the verifier.

By default all Keylime Agents listen for these revocation notifications (see the listen_notifications option in keylime.conf). Using the keys in the unzipped certificate package, Agents can check that the revocations are valid. Keylime Agents can also take actions in response to a valid revocation. You can configure these actions by putting additional files into the delivered zip file using –include.

Revocation actions are small Python scripts that will run on an Agent when a valid revocation is received. They should contain an execute function that takes one argument. This argument is a Python dictionary of metadata that can be used to tailor what the revocation action does. In the cert package mode, Keylime will specify the certificate serial number and common name (aka UUID) of the node that has failed its integrity check inside this metadata passed to the revocation action. For example, you can use this info to revoke the the offending X509 certificate.

One subtlety to revocation actions is that they are not intended for the Agent that has been revoked. If an Agent has failed its integrity check, then we really can’t trust that it won’t ignore the revocations and do arbitrarily malicious things. So, revocation actions are for other well-behaving Agents in the system to take action against the revoked Agent. For example, by revoking its certificate as described above or firewalling it from the network, etc.

There are several revocation actions implemented in Keylime that you can look at to get an idea of how to write your own. See the files starting with local_action in https://github.com/keylime/keylime/tree/master/auto-ipsec/libreswan/src

There are some conventions to specifying revocation actions. As described above, their names must start with local_action to be executed. They also must be listed (without .py extensions) in a comma separated list in a file called action_list in the zip file. For example to run local_action_a.py and local_action_b.py the action_list file should contain local_action_a,local_action_b.

So far we’ve described all the details of this in fine detail, but much of this automation will happen by default.

Certificate Package Example

Let’s put all of the above together with an example.

For the following example, we will provision some SSH keys onto the Agent.

  1. Create a directory to host the files and autorun.sh script. For this example, we will use the directory payload

  2. Create an autorun.sh script in the payload directory:

#!/bin/bash

# this will make it easier for us to find our own cert
ln -s `ls *-cert.crt | grep -v Revocation` mycert.crt

mkdir -p /root/.ssh/
cp payload_id_rsa* /root/.ssh/
chmod 600 /root/.ssh/payload_id_rsa*
  1. Copy the files you wish to provision into the payload directory.

$ ls payload/
autorun.sh
payload_id_rsa.pub
payload_id_rsa

Send the files using the Keylime Tenant tool:

keylime_tenant -t <agent-ip> --cert myca --include payload

Recall that the –cert option tells Keylime to create a certificate authority at the default location /var/lib/keylime/ca and give this machine an X509 identity with its UUID. Keylime will also create a revocation notifier certificate for this CA and make it available to the verifier. Finally, the –include option tells Keylime to securely deliver the files in the specified directory (payload in our case) along with the X509 certs to the targeted Agent machine.

If the enrolment was been successful, you will be able to see the contents of the payload directory in /var/lib/keylime/secure/unzipped along with the certs and included files. You should also see the SSH keys we included made in /root/.ssh directory from where the autorun.sh script was ran.

Now, let’s extend this example with revocation. In this example, we’re going to execute a simple revocation action on the node that was revoked.

It is also possible to configure scripts for execution should a node fail any given criteria (IMA measurements, for example).

To configure this, we will use our payload directory again.

First create a Python script with the preface of local_action

For example local_action_rm_ssh.py

Within this script create an execute function:

import os
import json
import keylime.ca_util as ca_util
import keylime.secure_mount as secure_mount

async def execute(event):
    if event['type'] != 'revocation':
        return

    json_meta = json.loads(event['meta_data'])
    serial = json_meta['cert_serial']

    # load up my own cert
    secdir = secure_mount.mount()
    mycert = ca_util.load_cert_by_path(f'{secdir}/unzipped/mycert.crt')

    # is this revocation meant for me?
    if serial == mycert.serial_number:
        os.remove("/root/.ssh/payload_id_rsa")
        os.remove("/root/.ssh/payload_id_rsa.pub")

Next, in the payload directory create the action_list file containing local_action_rm_ssh (remember not to put the .py extension).

Warning

In the above example, the node that fails its integrity check is the same one that we’re expecting to run the revocation action to delete the key. Since the node is potentially compromised, we really can’t expect that it will actually do this and not just ignore the revocation. A more realistic scenario for SSH keys is to provision one node with the SSH key generated as above, then provision a second server and add payload_id_rsa.pub to .ssh/authorized_keys using an autorun script. At this point, you can SSH from the first server to the second one. Should the first machine fail its integrity, then an revocation action on the second server can remove the compromised first machine from its list of Secure machines in .ssh/authorized_keys

Many actions can be executed based on CA revocation. For more details and examples, please refer to the Agent Revocation page.

Agent Revocation

Warning

This page is still under development and not complete. It will be so until this warning is removed.

Design of Keylime

Overview of Keylime

Keylime mainly consists of an agent, two server components (verifier and registrar) and a commandline tool the tenant.

Agent

The agent is a service that runs on the operating system that should be attested. It communicates with the TPM to enroll the AK and to generate quotes and collects the necessary data like the UEFI and IMA event logs to make state attestation possible.

The agent provides an interface to provision the device further once it was attested successfully for the first time using the secure payload mechanism. For more details see: Secure Payloads.

It is possible for the agent to listen to revocation events that are sent by the verifier if an agent attestation failed. This is useful for environments where attested systems directly communicate with each other and require that the other systems are trusted. In this case a revocation message might change local policies so that the compromised system cannot access any resources from other systems.

Registrar

The agent registers itself in the registrar. The registrar manages the agent enrollment process which includes getting an UUID for the agent, collecting the EKpub, EK certificate and AKpub from an agent and verifying that the AK belongs to the EK (using MakeCredential and ActivateCredential).

Once an agent has been registered in the registrar, it is ready to be enrolled for attestation. The tenant can use the EK certificate to verify to verify the trustworthiness of the TPM. Both the tenant and verifier

Note

If EK or AK are mentioned outside of internal TPM signing operations, it usually references the EKpub or AKpub because it should not be possible extract the private keys out of the TPM.

Note

The Keylime agent currently generates a AK on every startup and sends the EK and EK certificate. This is done to keep then design simple by not requiring a third party to verify the EK. The EK (and EK certificate) is required to verify the authenticity of the AK once and Keylime does not require a new AK but currently registration only with an AK is not enabled because the agent does not implement persisting the AK.

Verifier

The verifier implements the actual attestation of an agent and sends revocation messages if an agent leaves the trusted state.

Once an agent is registered for attestation (using the tenant or the API directly) the verifier continuously pulls the required attestation data from the agent. This can include: a quote over the PCRs, the PCR values, NK public key, IMA log and UEFI event log. After that the quote is validated additional validation of the data can be configured.

Static PCR values

The tpm_policy allows for simple checking of PCR values against a known good allowlist. In most cases this is only useful when the boot chain does not change often, there is a way to retrieve the values beforehand and the UEFI event log is unavailable. More information can be found in User Selected PCR Monitoring.

Measured Boot using the UEFI Event Log

On larger deployments it is not feasible to collect golden values for the PCR values used for measured boot. To make attestation still possible Keylime includes a policy engine for validating the UEFI event log. This is the preferred method because static PCR values are fragile because they change if something in the boot chain is updated (firmware, Shim, GRUB, Linux kernel, initrd, …). More information can be found in Use Measured Boot.

IMA validation

Keylime allows to verify files measured by IMA against either a provided allowlist or a signature. This makes it for example easy to attest all files that were executed by root. More information can be found in Run-time Integrity Monitoring.

Tenant

The tenant is a commandline management tool shipped by Keylime to manage agents. This includes adding or removing the agent from attestation, validation of the EK certificate against a cert store and getting the status of an agent. It also provides the necessary tools for the payload mechanism and revocation actions.

For all the features of the tenant see the output of keylime_tenant --help.

Threat Model

Keylime was originally developed with the intention of using it in combination with hypervisors to protect the VMs against by using the vTPM support in Xen. vTPM support for TPM2.0 was never implemented into Keylime and swtpm+libvirt never supported it, so this model no longer fits. Keylime is commonly used either on bare metal hardware or in VMs where the TPM is emulated but from VM side treated the same as a hardware TPM. Therefore the common threat model is defined on the latter use case.

Note

The term vTPM can be confusing because it originally described the deep quote feature in Xen which Keylime used for TPM 1.2. Now it commonly refers to a software implementation of a TPM (e.g. swtpm) or the Virtual TPM Proxy Driver in the Linux kernel.

From Keylime’s perspective the core hardware like CPU, memory, motherboard is trusted, because it does not provide mechanisms to detect tampering with the hardware itself. Keylime chains its root of trust into the TPM therefore the TPM is deemed in general trustworthy. This trust is verified using the EK or EK certificate.

The goal of Keylime is to attest the state of a running system. For this to work the entire boot chain has to be verified. The UEFI with Secure Boot enabled firmware and CRTM are generally trusted because it provides the UEFI event log and the API for other EFI applications to use the TPM. All the other applications in the boot chain are either measured by the firmware or the application that loads them (e.g. GRUB2 loads the kernel). The threat model does not require to trust arbitrary EFI applications during the boot process because it can be verified after boot what was executed.

The threat model includes that an adversary has full control over the network and can either sent rouge messages, drop or modify them. Also the Keylime agent and running operating system itself is not deemed trustworthy by default. Only after the successful initial attestation the system is deemed trustworthy, but still can leave the trusted state at any moment and is therefore continuously attested.

Types of Attacks to detect

Keylime tries to detect the following attacks.

TPM Quote Manipulation

Because the TPM is the root-of-trust for Keylime, it ensures that the quote is valid. This is vital for all the other attestation steps because the quote is used to validate the data.

Keylime ensures this through three steps:

  • EK validation: The tenant allows Keylime to verify the EK certificate against the CAs of hardware manufacturers or add custom validation steps. This is done to ensure that the EK belongs to an actual hardware TPM or a trusted software TPM.

  • AK enrollment: Using the TPM commands MakeCredential, ActivateCredential and enforcing certain key properties (restricted, user with auth, sign encrypt, fixed TPM, fixed parent and sensitive data origin) Keylime ensures that the used AK belongs to the provided EK and has the right properties for signing quotes.

  • Quote validation: Each quote generated by the TPM is verified with the AK provided during agent registration. The verifier provides a fresh nonce that is included in the quote to prohibit replay attacks.

Modification of the boot process

Checking the security of the running system does only make sense if it can be ensured that the system was correctly booted. Therefore Keylime provides two ways to allow users to verify the entire boot chain up to the running system: static PCR value checks (User Selected PCR Monitoring) and the measured boot policy engine (Use Measured Boot).

Runtime file and system integrity

Keylime can attest the state of a Linux system and the files using the Linux Integrity Measurement Architecture (IMA). Therefore Keylime can be used to remotely check for attacks that IMA detects.

Rest API’s

All Keylime APIs use REST (Representational State Transfer).

Authentication

Most API interactions are secured using mTLS connections. There are up to three different CAs involved. (The revocation process also uses a CA, but this is a different to those CAs)

Verifier CA (CV CA)

This CA is used by the verifier to authenticate connections to it. The tenant requires a certificate from this CA to add/delete/list agents at the CV.

Registrar CA

This CA is used by the registrar to authenticate connections to it. The tenant and the verifier require certificate from this CA to get or delete agent information from the registrar. By default it is the same CA as the CV CA.

Note that the API endpoints from the registrar for the agent are unprotected by design.

Agent Keylime CA

The agent runs a HTTPS server and provides it’s certificate to the registrar (mtls_cert). All connections done to the agent are verified against the CA specified in the keylime_ca option. This ensures that only trusted systems can interact with the agents. By default the CV CA is used, but the CA certificate (cacert.crt) needs to be copied to the agents.

Registrar, tenant and verifier can configure this CA separately using the agent_mtls_* options.

RESTful API for Keylime (v2.1)

Keylime API is versioned. More information can be found here: https://github.com/keylime/enhancements/blob/master/45_api_versioning.md

Warning

API version 1.0 will no longer be officially supported starting with Keylime 6.4.0.

General responses

ANY /

Generic fields in responses

Response JSON Object:
  • code (int) – HTTP status code

  • status (string) – textual context of that status

  • results (object) – Holds the actual data.

Cloud verifier (CV)

GET /v2.1/agents/{agent_id:UUID}

Get status of agent agent_id from CV

Example response:

{
  "code": 200,
  "status": "Success",
  "results": {
    "operational_state": 7,
    "v": "yyNnlWwFRz1ZUzSe2YEpz9A5urtv6oywgttTF7VbBP4=",
    "ip": "127.0.0.1",
    "port": 9002,
    "tpm_policy": "{\"22\": [\"0000000000000000000000000000000000000001\", \"0000000000000000000000000000000000000000000000000000000000000001\", \"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001\", \"ffffffffffffffffffffffffffffffffffffffff\", \"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\", \"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\"], \"15\": [\"0000000000000000000000000000000000000000\", \"0000000000000000000000000000000000000000000000000000000000000000\", \"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"], \"mask\": \"0x408000\"}",
    "vtpm_policy": "{\"23\": [\"ffffffffffffffffffffffffffffffffffffffff\", \"0000000000000000000000000000000000000000\"], \"15\": [\"0000000000000000000000000000000000000000\"], \"mask\": \"0x808000\"}",
    "meta_data": "{}",
    "allowlist_len": 0,
    "mb_refstate_len": 0,
    "accept_tpm_hash_algs": [
      "sha512",
      "sha384",
      "sha256",
      "sha1"
    ],
    "accept_tpm_encryption_algs": [
      "ecc",
      "rsa"
    ],
    "accept_tpm_signing_algs": [
      "ecschnorr",
      "rsassa"
    ],
    "hash_alg": "sha256",
    "enc_alg": "rsa",
    "sign_alg": "rsassa",
    "verifier_id": "default",
    "verifier_ip": "127.0.0.1",
    "verifier_port": 8881,
    "severity_level": 6,
    "last_event_id": "qoute_validation.quote_validation"
  }
}
Response JSON Object:
  • code (int) – HTTP status code

  • status (string) – Status as string

  • operational_state (int) – Current state of the agent in the CV. Defined in https://github.com/keylime/keylime/blob/master/keylime/common/states.py

  • v (string) – V key for payload base64 encoded. Decoded length is 32 bytes

  • ip (string) – Agents contact ip address for the CV

  • port (string) – Agents contact port for the CV

  • tpm_policy (string) – Static PCR policy and mask for TPM

  • vtpm_policy (string) – Static PCR policy and mask for vTPM

  • meta_data (string) – Metadata about the agent. Normally contains certificate information if a CA is used.

  • allowlist_len (int) – Length of the allowlist.

  • mb_refstate_len (int) – Length of the measured boot reference state policy.

  • accept_tpm_hash_algs (list[string]) – Accepted TPM hashing algorithms. sha1 must be enabled for IMA validation to work.

  • accept_tpm_encryption_algs (list[string]) – Accepted TPM encryption algorithms.

  • accept_tpm_signing_algs (list[string]) – Accepted TPM signing algorithms.

  • hash_alg (string) – Used hashing algorithm.

  • enc_alg (string) – Used encryption algorithm.

  • sign_alg (string) – Used signing algorithm.

  • verifier_id (string) – Name of the verifier that is used. (Only important if multiple verifiers are used)

  • verifier_ip (string) – IP of the verifier that is used.

  • verifier_port (int) – Port of the verifier that is used.

  • severity_level (int) – Severity level of the agent. Might be null. Levels are the numeric representation of the severity labels.

  • last_event_id (string) – ID of the last revocation event. Might be null.

POST /v2.1/agents/{agent_id:UUID}

Add new agent instance_id to CV.

Example request:

{
  "v": "3HZMmIEc6yyjfoxdCwcOgPk/6X1GuNG+tlCmNgqBM/I=",
  "cloudagent_ip": "127.0.0.1",
  "cloudagent_port": 9002,
  "tpm_policy": "{\"22\": [\"0000000000000000000000000000000000000001\", \"0000000000000000000000000000000000000000000000000000000000000001\", \"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001\", \"ffffffffffffffffffffffffffffffffffffffff\", \"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\", \"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\"], \"15\": [\"0000000000000000000000000000000000000000\", \"0000000000000000000000000000000000000000000000000000000000000000\", \"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"], \"mask\": \"0x408000\"}",
  "vtpm_policy": "{\"23\": [\"ffffffffffffffffffffffffffffffffffffffff\", \"0000000000000000000000000000000000000000\"], \"15\": [\"0000000000000000000000000000000000000000\"], \"mask\": \"0x808000\"}",
  "allowlist": "{}",
  "mb_refstate": "null",
  "ima_sign_verification_keys": "[]",
  "metadata": "{\"cert_serial\": 71906672046699268666356441515514540742724395900, \"subject\": \"/C=US/ST=MA/L=Lexington/O=MITLL/OU=53/CN=D432FBB3-D2F1-4A97-9EF7-75BD81C00000\"}",
  "revocation_key": "-----BEGIN PRIVATE KEY----- (...) -----END PRIVATE KEY-----\n",
  "accept_tpm_hash_algs": [
    "sha512",
    "sha384",
    "sha256",
    "sha1"
  ],
  "accept_tpm_encryption_algs": [
    "ecc",
    "rsa"
  ],
  "accept_tpm_signing_algs": [
    "ecschnorr",
    "rsassa"
  ],
  "supported_version": "2.0"
}
Request JSON Object:
  • v (string) – V key for payload base64 encoded. Decoded length is 32 bytes.

  • cloudagent_ip (string) – Agents contact ip address for the CV.

  • cloudagent_port (string) – Agents contact port for the CV.

  • tpm_policy (string) – Static PCR policy and mask for TPM. Is a string encoded dictionary that also includes a mask for which PCRs should be included in a quote.

  • vtpm_policy (string) – Static PCR policy and mask for vTPM. Same as tpm_policy.

  • allowlist (string) – Allowlist JSON object string encoded.

  • mb_refstate (string) – Measured boot reference state policy.

  • ima_sign_verification_keys (string) – IMA signature verification public keyring JSON object string encoded.

  • metadata (string) – Metadata about the agent. Contains cert_serial and subject if a CA is used with the tenant.

  • revocation_key (string) – Key which is used to sign the revocation message of the agent.

  • accept_tpm_hash_algs (list[string]) – Accepted TPM hashing algorithms. sha1 must be enabled for IMA validation to work.

  • accept_tpm_encryption_algs (list[string]) – Accepted TPM encryption algorithms.

  • accept_tpm_signing_algs (list[string]) – Accepted TPM signing algorithms.

  • supported_version (string) – supported API version of the agent. v prefix must not be included.

DELETE /v2.1/agents/{agent_id:UUID}

Terminate instance agent_id.

Example response:

{
  "code": 200,
  "status": "Success",
  "results": {}
}
PUT /v2.1/agents/{agent_id:UUID}/reactivate

Start agent agent_id (for an already bootstrapped agent_id node)

PUT /v2.1/agents/{agent_id:UUID}/stop

Stop cv polling on agent_id, but don’t delete (for an already started agent_id). This will make the agent verification fail.

Cloud Agent

GET /v2.1/keys/pubkey

Retrieves agents public key.

Example response:

{
  "code": 200,
  "status": "Success",
  "results": {
    "pubkey": "-----BEGIN PUBLIC KEY----- (...) -----END PUBLIC KEY-----\n"
  }
}
Response JSON Object:
  • pubkey (string) – Public rsa key of the agent used for encrypting V and U key.

GET /version

Returns what API version the agent supports. This endpoint might not be implemented by all agents.

Example response:

{
  "code": 200,
  "status": "Success",
  "results": {
    "supported_version": "2.0"
  }
}
Response JSON Object:
  • supported_version (string) – The latest version the agent supports.

POST /v2.1/keys/vkey

Send v_key to node.

Example request:

{
  "encrypted_key": "MN/F33jjuLiIuRH8fF7pMtw6Hoe2KG10zg+/xuuZLa5d1WB2aR6feVCwknZDe/dhG51yB0tKau8fCNUz8KMxyWoFkalIY4vVG6DNpLouDjb+vMvI6RmVmCBwO5zx6R802wK2z2yUbcn11TU/k2zHq34CNFIgI5pQu7cnLMzCLW6NLEp8N0IOQL6D+uV9emkheJH1g40xYwUaKoABWjZeaJN5dvKwbkpIf2m+CROmCNPCidh87J0g7BENUvlSUO1FPfRjch4kyxLrp+aMu9zmzF/tZErh1zk+nUamtrgl25pEImw+Cn9RIVTd6fBkmzlGzch5foAqZCyZ0AhQ0ONuWw==",
}
Request JSON Object:
  • encrypted_key (string) – V key encrypted with agents public key base64 encoded.

POST /v2.1/keys/ukey

Send u_key to node (with optional payload)

Example request:

{
  "auth_tag" : "3876c08b30c16c4140ee04300bb4262bbcc9034d8a2ed8c90784f13b484a570bf9da3d5c372141bd16d85de05c4c7cce",
  "encrypted_key": "iAckMZgZc8r43pF0iW8iwwAorD+rvnvF7AShhlz6+am+ryqW+907UynOrWrIrAseyVRE7ouHpr547gnwfF7oKeBFlEdWnE6FbQl9o6tk86BzQy3PImBLxJD/y/MmSuNR5pGQwZCueKI0ji3Nqq6heOgSvnMRC0PHgyumOsYiAnbDNyryvfwO4HsqdqMcEsBu1IVzU3EtJWhfQ8i/UpvHy6Jq4bBh+mw5HZwmK93bmsLXNKgjPWAicsCZINUAPVMCUL7dcDd4zijsBxMxiZF7Js7V25wKKFer2zqKsE5omLy9sKotFfWjgaROPLrKXxuDgNmlONJnD0btLZBa9T+mmA==",
  "payload": "WcXpUr4G9yfvVaojNx6K2XZuDYRkFoZQhHrvZB+TKZqsq41g"
}
Request JSON Object:
  • auth_tag (string) – HMAC calculated with K key as key and UUID as data, using SHA-384 as the underlying hash algorithm

  • encrypted_key (string) – U key encrypted with agents public key base64 encoded

  • payload (string) – (optional) payload encrypted with K key base64 encoded.

GET /v2.1/keys/verify

Get confirmation of bootstrap key derivation

Example request:

/v2.1/keys/verify?challenge=1234567890ABCDEFHIJ
Parameters:
  • challenge (string) – 20 character random string with [a-Z,0-9] as symbols.

Example response:

{
  "code": 200,
  "status": "Success",
  "results": {
    "hmac": "719d992fb7d2a0761785fd023fe1cf8a584b835e465e71e2ef2632ff4e9938c080bdefba26194d8ea69dd7f9adee6c18"
  }
}
Response JSON Object:
  • hmac (string) – hmac with K key as key and the challenge

GET /v2.1/quotes/integrity

Get integrity quote from node

Example request:

/v2.1/quotes/integrity?nonce=1234567890ABCDEFHIJ&mask=0x10401&partial=0
Parameters:
  • nonce (string) – 20 character random string with [a-Z,0-9] as symbols.

  • mask (string) – Mask for what PCRs from the TPM are included in the quote.

  • partial (string) – Is either “0” or “1”. If set to “1” the public key is excluded in the response.

  • ima_ml_entry (string) – (optional) Line offset of the IMA entry list. If not present, 0 is assumed.

Example Response:

{
  "code": 200,
  "status": "Success",
  "results": {
    "quote": "r/1RDR4AYABYABPihP2yz+HcGF0vD0c4qiKt4nvSOAARURVNUAAAAAAAyQ9AAAAAAAAAAAAEgGRAjABY2NgAAAAEABAMAAAEAFCkk4YmhQECgWR+MnHqT9zftc3J8:ABQABAEAQ8IwX6Ak83zGhF6w8vOKOxsyTbxACQakYWGJaan3ewf+2O9TtiH5TLB1PXrPdhknsR/yx6OVUze9jTDvML9xkkK1ghXObCJ5gH+QX0udKfrLacm/iMds28SBtVO0rjqDIoYqGgXhH2ZhwGNDwjRCp6HquvtBe7pGEgtZlxf7Hr3wQRLO3FtliBPBR6gjOo7NC/uGsuPjdPU7c9ls29NgYSqdwShuNdRzwmZrF57umuUgF6GREFlxqLkGcbDIT1itV4zJZtI1caLVxqiH0Qv3sNqlNLsSHggkgc5S2EvNqwv/TsEZOq/leCoLtyVGYghPeGwg0RJfbe8cdyBWCQ6nOA==:AQAAAAQAAwAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAUABdJ/ntmsqy2aDi6NhKnLKz4k4uEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
    "hash_alg": "sha256",
    "enc_alg": "rsa",
    "sign_alg": "rsassa",
    "pubkey": "-----BEGIN PUBLIC KEY----- (...) -----END PUBLIC KEY-----\n"
    "boottime": 123456,
    "ima_measurement_list": "10 367a111b682553da5340f977001689db8366056a ima-ng sha256:94c0ac6d0ff747d8f1ca7fac89101a141f3e8f6a2c710717b477a026422766d6 boot_aggregate\n",
    "ima_measurement_list_entry": 0,
    "mb_measurement_list": "AAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAABTcGVjIElEIEV2ZW50MDMAAAAAAAACAAIBAAAACwAgAAAAAAAACAAAAAEAAAALAJailtIk8oXGe [....]"
  }
}
Response JSON Object:
  • quote (string) – TPM integrity quote

  • hash_alg (string) – Used hash algorithm used in the quote (e.g. sha1, sha256, sha512).

  • enc_alg (string) – Encryption algorithm used in the quote (ecc, rsa).

  • sign_alg (string) – Signing algorthm used in the quote (rsassa, rsapss, ecdsa, ecdaa or ecschnorr).

  • pubkey (string) – PEM encoded public portion of the NK (digest is measured into PCR 16).

  • boottime (int) – Seconds since the system booted

  • ima_measurement_list (string) – (optional) IMA entry list. Is included if IMA_PCR (10) is included in the mask

  • ima_measurement_list_entry (int) – (optional) Starting line offset of the IMA entry list returned

  • mb_measurement_list (string) – (optional) UEFI Eventlog list base64 encoded. Is included if PCR 0 is included in the mask

Quote format: The quote field contains the quote, the signature and the PCR values that make up the quote.

QUOTE_DATA := rTPM_QUOTE:TPM_SIG:TPM_PCRS
TPM_QUOTE  := base64(TPMS_ATTEST)
TPM_SIG    := base64(TPMT_SIGNATURE)
TPM_PCRS   := base64(tpm2_pcrs) // Can hold more that 8 PCR entries. This is a data structure generated by tpm2_quote
GET /v2.1/quotes/identity

Get identity quote from node

Example request:

/v2.1/quotes/identity?nonce=1234567890ABCDEFHIJ
Parameters:
  • nonce (string) – 20 character random string with [a-Z,0-9] as symbols.

Example response:

{
  "code": 200,
  "status": "Success",
  "results": {
    "quote": "r/1RDR4AYABYABPihP2yz+HcGF0vD0c4qiKt4nvSOAARURVNUAAAAAAAyQ9AAAAAAAAAAAAEgGRAjABY2NgAAAAEABAMAAAEAFCkk4YmhQECgWR+MnHqT9zftc3J8:ABQABAEAQ8IwX6Ak83zGhF6w8vOKOxsyTbxACQakYWGJaan3ewf+2O9TtiH5TLB1PXrPdhknsR/yx6OVUze9jTDvML9xkkK1ghXObCJ5gH+QX0udKfrLacm/iMds28SBtVO0rjqDIoYqGgXhH2ZhwGNDwjRCp6HquvtBe7pGEgtZlxf7Hr3wQRLO3FtliBPBR6gjOo7NC/uGsuPjdPU7c9ls29NgYSqdwShuNdRzwmZrF57umuUgF6GREFlxqLkGcbDIT1itV4zJZtI1caLVxqiH0Qv3sNqlNLsSHggkgc5S2EvNqwv/TsEZOq/leCoLtyVGYghPeGwg0RJfbe8cdyBWCQ6nOA==:AQAAAAQAAwAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAUABdJ/ntmsqy2aDi6NhKnLKz4k4uEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
    "hash_alg": "sha256",
    "enc_alg": "rsa",
    "sign_alg": "rsassa",
    "pubkey": "-----BEGIN PUBLIC KEY----- (...) -----END PUBLIC KEY-----\n"
    "boottime": 123456
  }
}
Response JSON Object:
  • quote (string) – See quotes/integrity

  • hash_alg (string) – See quotes/integrity

  • enc_alg (string) – See quotes/integrity

  • sign_alg (string) – See quotes/integrity

  • pubkey (string) – See quotes/integrity

  • boottime (int) – See quotes/integrity

Cloud Registrar

GET /v2.1/agents/

Get ordered list of registered agents

Example response:

{
  "code": 200,
  "status": "Success",
  "results": {
    "uuids": [
      "5e600bce-a5cb-4f5a-bf08-46d0b45081c5",
      "6dab10e4-6619-4ff9-9062-ee6ad23ec24d",
      "d432fbb3-d2f1-4a97-9ef7-75bd81c00000"
    ]
  }
}
GET /v2.1/agents/{agent_id:UUID}

Get EK certificate, AIK and optinal contact ip and port of agent agent_id.

Example response:

{
  "code": 200,
  "status": "Success",
  "results": {
    "aik_tpm": "ARgAAQALAAUAcgAAABAAFAALCAAAAAAAAQDjZ4J2HO7ekIONAX/eYIzt7ziiVAqE/1D7I9oEwIE88dIfqH0FQLJAg8u3+ZOgsJDQr9HiMhZRPhv8hRuia8ULdAomyOFA1cVzlBF+xcPUEemOIofbvcBNAoTY/x49r8LpqAEUBBiUeOniQbjfRaV2S5cEAA92wHLQAPLF9Sbf3zNxCnbhtRkEi6C3NYl8/FJqyu5Z9vvwEBBOFFTPasAxMtPm6a+Z5KJ4rDflipfaVcUvTKLIBRI7wkuXqhTR8BeIByK9upQ3iBo+FbYjWSf+BaN+wodMNgPbzxyL+tuxVqiPefBbv+sTWVxmYfo5i84FlbNOAW3APH8c+jZ3tgbt",
    "ek_tpm": "AToAAQALAAMAsgAgg3GXZ0SEs/gakMyNRqXXJP1S124GUgtk8qHaGzMUaaoABgCAAEMAEAgAAAAAAAEA0YwlPPIoXryMvbD5cIokN9OkljL2mV1oDxy7ETBXBe1nL9OWrLNO8Nbf8EaSNCtYCo5iqCwatnVRMPqNXcX8mQP0f/gDAqXryb+F192IJLKShHYSN32LJjCYOKrvNX1lrmr377juICFSRClE4q+pCfzhNj0Izw/eplaAI7gq41vrlnymWYGIEi4McErWG7qwr7LR9CXwiM7nhBYGtvobqoaOm4+f6zo3jQuks/KYjk0BR3mgAec/Qkfefw2lgSSYaPNl/8ytg6Dhla1LK8f7wWy/bv+3z7L11KLr8DZiFAzKBMiIDfaqNGYPhiFLKAMJ0MmJx63obCqx9z5BltV5YQ==",
    "ekcert": "MIIEGTCCAoGgAwIBAgIBBTANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDEw1zd3RwbS1sb2NhbGNhMB4XDTIxMDQwOTEyNDAyNVoXDTMxMDQwNzEyNDAyNVowODE2MDQGA1UEAxMtZmVkb3JhMzM6NDdjYzJlMDMtNmRmMi00OGMyLWFmNGUtMDg1MWY1MWQyODJiMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0YwlPPIoXryMvbD5cIokN9OkljL2mV1oDxy7ETBXBe1nL9OWrLNO8Nbf8EaSNCtYCo5iqCwatnVRMPqNXcX8mQP0f/gDAqXryb+F192IJLKShHYSN32LJjCYOKrvNX1lrmr377juICFSRClE4q+pCfzhNj0Izw/eplaAI7gq41vrlnymWYGIEi4McErWG7qwr7LR9CXwiM7nhBYGtvobqoaOm4+f6zo3jQuks/KYjk0BR3mgAec/Qkfefw2lgSSYaPNl/8ytg6Dhla1LK8f7wWy/bv+3z7L11KLr8DZiFAzKBMiIDfaqNGYPhiFLKAMJ0MmJx63obCqx9z5BltV5YQIDAQABo4HNMIHKMBAGA1UdJQQJMAcGBWeBBQgBMFIGA1UdEQEB/wRIMEakRDBCMRYwFAYFZ4EFAgEMC2lkOjAwMDAxMDE0MRAwDgYFZ4EFAgIMBXN3dHBtMRYwFAYFZ4EFAgMMC2lkOjIwMTkxMDIzMAwGA1UdEwEB/wQCMAAwIgYDVR0JBBswGTAXBgVngQUCEDEOMAwMAzIuMAIBAAICAKIwHwYDVR0jBBgwFoAUaO+9FEi5yX/GEnU+Vc6b3Si6JeAwDwYDVR0PAQH/BAUDAwcgADANBgkqhkiG9w0BAQsFAAOCAYEAaP/jI2i/hXDrthtaZypQ8VUG5AWFnMDtgiMhDSaKwOBfyxiUiYMTggGYXLOXGIu1SJGBtRJsh3QSYgs2tJCnntWF9Jcpmk6kIW/MC8shE+hdu/gQZKjAPZS4QCLIldv+GVZdNYEIv2FYDsKl6Bq1qUsYhAb7z29Nu1itpdvja2qy7ODJ0u+ThccBuH60VGFclFdJg19dvVQMnffxzjwxxJTMnVPmGoEdR94O0z7yxvqQ22+ITD9s1c3AfWcV+yLEpHqhXRqtKGdkAM5kU85kEs/ZPTLNutJHmF0/Vk9W2pRym8SrUe8G6mwxVW8lP9M7fhovKTzoXVFW3gQWQeUxhvWOncXxtARFLp/+f2mzGBRWxIslW17vpZ3QLlCdJ2C7P3U8x2tvkuyyDfz3/pq+8ECupZhdSvpHlBnWvqs1tAWKW0qI9d0xNYjj3Kfl3Lfy7kqqe6FIkvbDlVhw3vnJlclW+M6D86jBulL9ze+3zyMxy2z8m7UHiLCbamSe6m7W",
    "mtls_cert": "-----BEGIN CERTIFICATE----- (...) -----END CERTIFICATE-----",
    "ip": "127.0.0.1",
    "port": 9002,
    "regcount": 1
  }
}
Response JSON Object:
  • aik_tpm (string) – base64 encoded AIK. The AIK format is TPM2B_PUBLIC from tpm2-tss.

  • ek_tpm (string) – base64 encoded EK. When a ekcert is submitted it will be the public key of that certificate.

  • ekcert (string) – base64 encoded EK certificate. Should be in DER format. Gets extracted from NV 0x1c00002.

  • mtls_cert (string) – Agent HTTPS server certificate. PEM encoded.

  • ip (string) – IPv4 address for contacting the agent. Might be null.

  • port (integer) – Port for contacting the agent. Might be null.

POST /v2.1/agents/{agent_id:UUID}

Add agent agent_id to registrar.

Example request:

{
  "ekcert": "MIIEGTCCAoGgAwIBAgIBBTANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDEw1zd3RwbS1sb2NhbGNhMB4XDTIxMDQwOTEyNDAyNVoXDTMxMDQwNzEyNDAyNVowODE2MDQGA1UEAxMtZmVkb3JhMzM6NDdjYzJlMDMtNmRmMi00OGMyLWFmNGUtMDg1MWY1MWQyODJiMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0YwlPPIoXryMvbD5cIokN9OkljL2mV1oDxy7ETBXBe1nL9OWrLNO8Nbf8EaSNCtYCo5iqCwatnVRMPqNXcX8mQP0f/gDAqXryb+F192IJLKShHYSN32LJjCYOKrvNX1lrmr377juICFSRClE4q+pCfzhNj0Izw/eplaAI7gq41vrlnymWYGIEi4McErWG7qwr7LR9CXwiM7nhBYGtvobqoaOm4+f6zo3jQuks/KYjk0BR3mgAec/Qkfefw2lgSSYaPNl/8ytg6Dhla1LK8f7wWy/bv+3z7L11KLr8DZiFAzKBMiIDfaqNGYPhiFLKAMJ0MmJx63obCqx9z5BltV5YQIDAQABo4HNMIHKMBAGA1UdJQQJMAcGBWeBBQgBMFIGA1UdEQEB/wRIMEakRDBCMRYwFAYFZ4EFAgEMC2lkOjAwMDAxMDE0MRAwDgYFZ4EFAgIMBXN3dHBtMRYwFAYFZ4EFAgMMC2lkOjIwMTkxMDIzMAwGA1UdEwEB/wQCMAAwIgYDVR0JBBswGTAXBgVngQUCEDEOMAwMAzIuMAIBAAICAKIwHwYDVR0jBBgwFoAUaO+9FEi5yX/GEnU+Vc6b3Si6JeAwDwYDVR0PAQH/BAUDAwcgADANBgkqhkiG9w0BAQsFAAOCAYEAaP/jI2i/hXDrthtaZypQ8VUG5AWFnMDtgiMhDSaKwOBfyxiUiYMTggGYXLOXGIu1SJGBtRJsh3QSYgs2tJCnntWF9Jcpmk6kIW/MC8shE+hdu/gQZKjAPZS4QCLIldv+GVZdNYEIv2FYDsKl6Bq1qUsYhAb7z29Nu1itpdvja2qy7ODJ0u+ThccBuH60VGFclFdJg19dvVQMnffxzjwxxJTMnVPmGoEdR94O0z7yxvqQ22+ITD9s1c3AfWcV+yLEpHqhXRqtKGdkAM5kU85kEs/ZPTLNutJHmF0/Vk9W2pRym8SrUe8G6mwxVW8lP9M7fhovKTzoXVFW3gQWQeUxhvWOncXxtARFLp/+f2mzGBRWxIslW17vpZ3QLlCdJ2C7P3U8x2tvkuyyDfz3/pq+8ECupZhdSvpHlBnWvqs1tAWKW0qI9d0xNYjj3Kfl3Lfy7kqqe6FIkvbDlVhw3vnJlclW+M6D86jBulL9ze+3zyMxy2z8m7UHiLCbamSe6m7W",
  "aik_tpm": "ARgAAQALAAUAcgAAABAAFAALCAAAAAAAAQCg5mMzNFqdlUbW8uI/GuMcIIvOXXTohHFTas59JlwrJQVed+5klWP+j7tI7492YPmCnoZvP4T4YdT1PN7tHHGfF81AeMnuw5GV5RkW/QeSD+ssB4f6AfuzYJgBkc28zKmpRRHUbwN4rb/HnJgRXdXsuIcnOqGcC39pD0kiu5TrN6hekjxTQtfAbIlQwwDwHCxKWdtH5x7avd15hqc6cBc2gjTQksXrk+OiMwOFTJ68n0qY+dQYuBTjE66YXn9S8cdU9sbjCTSdLRqFEpAyfkSV8F2An7N3DWNIA+PW/mVmd8XhPeYUoMlweXBOwc3e9zM9lZmMvregrFHKYc7CXChz",
  "mtls_cert": "-----BEGIN CERTIFICATE----- (...) -----END CERTIFICATE-----",
  "ip": "127.0.0.1",
  "port": "9002"
}
Request JSON Object:
  • ekcert (string) – base64 encoded EK certificate. Should be in DER format. Gets extracted from NV 0x1c00002.

  • aik_tpm (string) – base64 encoded AIK. The AIK format is TPM2B_PUBLIC from tpm2-tss.

  • mtls_cert (string) – Agent HTTPS server certificate. PEM encoded.

  • ip (string) – (Optional) contact IPv4 address for the verifier and tenant to use.

  • port (string) – (Optional) contact port for the verifier and tenant to use.

Example response:

{
  "code": 200,
  "status": "Success",
  "results": {
    "blob": "utzA3gAAAAEARAAgC/w9LP1PKZ9thEk+GkMg4m+tkc9TkavcvFiFL6xbXM2q2fTRyKmQnxuCJc0tQdgsRXMftGiKJyA/SUo8kGNVmcNfAQCs79kl9Ir49JJ8rfyMfDIqOuSVlu9PhxGUOeVzAdxyUmPxq5Qp0s431n/KeL/5nUaVXC+qpOftF4bmVtXwLGTTUbKtyT3GG+9ujkjiwHCQhSKTQ8HiuARgXXh13ntFsJ75PBD5dWauLTuciYZI/WQDVXAcgMnQNxodJUi9ir1GxJWz8zufjVQTVjrlgsgeBdOKbB6+H81K1d9prWhZaVLP+wIwO3YuWgtNHNi90E1z/dah2pzfUpLvJo3lNZ4bJgrJUR507AokGKIFm7EfOf+5WWWAvGxGtgqTJB27vgE0CVBLEuDUHoRcLVBi1Np4GGNTByalxbulg8x1eGtZyuQF"
  }
}
Response JSON Object:
  • blob (string) – base64 encoded blob containing the aik_tpm name and a challenge. Is encrypted with ek_tpm.

DELETE /v2.1/agents/{agent_id:UUID}

Remove agent agent_id from registrar.

Example response:

{
  "code": 200,
  "status": "Success",
  "results": {}
}
PUT /v2.1/agents/{agent_id:UUID}/activate

Activate physical agent agent_id

Example request:

{
  "auth_tag": "7087ba88746886262de743587ed97aea6b6e3f32755de5d85415c40feef3169bc58d38855ddb96e32efdd8745d0bdfef"
}
Request JSON Object:
  • auth_tag (string) – hmac containing the challenge from blob and the agent_id.

PUT /v2.1/agents/{agent_id:UUID}/vactivate

Activate virtual (vTPM) agent agent_id

Requires JSON Body:

{
  "deepquote" : b64,
}

Changelog

Changes between the different API versions.

Changes from v2.0 to v2.1

API version 2.1 was first implemented in Keylime 6.4.0.

  • Added ak_tpm field to POST /v2.1/agents/{agent_id:UUID} in cloud verifier.

  • Added mtls_cert field to POST /v2.1/agents/{agent_id:UUID} in cloud verifier.

  • Removed vmask parameter from

This removed the requirement for the verifier to connect to the registrar.

Changes from v1.0 to v2.0

API version 2.0 was first implemented in Keylime 6.3.0.

  • Added mTLS authentication to agent endpoints.

  • Added supported_version field to POST /v2.0/agents/{agent_id:UUID} in cloud verifier.

  • Added mtls_cert field to POST/GET /v2.0/agents/{agent_id:UUID} in registrar.

  • Added /version endpoint to agent. Note that this endpoint is not implemented by all agents.

  • Dropped zlib encryption for quote field data in GET /v2.0/quotes/integrity/GET /v2.0/quotes/identity.

KeyLime Development

Contributing

When contributing any keylime repository, please first discuss the change you wish to make via an issue in the relevant repository for your change or email to the keylime mailing list

Pull Request Process

  1. Create an issue outlining the fix or feature.

  2. Fork the keylime repository to your own github account and clone it locally.

  3. Hack on your changes.

  4. Update the README.md or documentation with details of changes to any interface, this includes new environment variables, exposed ports, useful file locations, CLI parameters and configuration values.

  5. Add and commit your changes with some descriptive text on the nature of the change / feature in your commit message. Also reference the issue raised at [1] as follows: Fixes #45. See the following link for more message types

  6. Ensure that CI passes, if it fails, fix the failures.

  7. Every pull request requires a review from the core keylime team

  8. If your pull request consists of more than one commit, please squash your commits as described in see Squash Commits.

Commit Message Guidelines

We follow the commit formatting recommendations found on Chris Beams’ How to Write a Git Commit Message article.

Well formed commit messages not only help reviewers understand the nature of the Pull Request, but also assists the release process where commit messages are used to generate release notes.

A good example of a commit message would be as follows:

Summarize changes in around 50 characters or less

More detailed explanatory text, if necessary. Wrap it to about 72
characters or so. In some contexts, the first line is treated as the
subject of the commit and the rest of the text as the body. The
blank line separating the summary from the body is critical (unless
you omit the body entirely); various tools like `log`, `shortlog`
and `rebase` can get confused if you run the two together.

Explain the problem that this commit is solving. Focus on why you
are making this change as opposed to how (the code explains that).
Are there side effects or other unintuitive consequences of this
change? Here's the place to explain them.

Further paragraphs come after blank lines.

- Bullet points are okay, too

- Typically a hyphen or asterisk is used for the bullet, preceded
  by a single space, with blank lines in between, but conventions
  vary here

If you use an issue tracker, put references to them at the bottom,
like this:

Resolves: #123
See also: #456, #789

Note the Resolves #123 tag, this references the issue raised and allows us to ensure issues are associated and closed when a pull request is merged.

Please refer to the github help page on message types for a complete list of issue references.

Squash Commits

Should your pull request consist of more than one commit (perhaps due to a change being requested during the review cycle), please perform a git squash once a reviewer has approved your pull request.

A squash can be performed as follows. Let’s say you have the following commits:

initial commit
second commit
final commit

Run the command below with the number set to the total commits you wish to squash (in our case 3 commits):

git rebase -i HEAD~3

You default text editor will then open up and you will see the following:

pick eb36612 initial commit
pick 9ac8968 second commit
pick a760569 final commit

# Rebase eb1429f..a760569 onto eb1429f (3 commands)

We want to rebase on top of our first commit, so we change the other two commits to squash:

pick eb36612 initial commit
squash 9ac8968 second commit
squash a760569 final commit

After this, should you wish to update your commit message to better summarise all of your pull request, run:

git commit --amend

You will then need to force push (assuming your initial commit(s) were posted to github):

git push origin your-branch --force

Docker Development Environment

The following is a guide to mounting your local repository as a Docker volume and performing a test run using a TPM simulator. This will replicate the same test that occurs within the KeyLime CI gate for keylime.

This requires a working installation of Docker. See your distributions guide on how to set that up.

As an example, on Fedora 29:

sudo dnf -y install dnf-plugins-core
sudo dnf install docker-ce docker-ce-cli containerd.io
sudo usermod -aG docker $USER
sudo systemctl enable docker
sudo systemctl start docker

Note: login and out of your shell, if you want to run docker as $USER

Save the following script to your local machine (tip: create an alias to call the script in an easy to remember way):

#!/bin/bash

# Your local keylime (you should likely change this)
REPO="/home/${USER}/keylime"

# keylime images
tpm12image="lukehinds/keylime-ci-tpm12"
tpm12tag="v550"
tpm20image="lukehinds/keylime-ci-tpm20"
tpm20tag="v301"

echo -e "Grabbing latest images"

docker pull ${tpm12image}:${tpm12tag}
docker pull ${tpm20image}:${tpm20tag}

function tpm1 {
    container_id=$(mktemp)
    docker run --detach --privileged \
        -v $REPO:/root/keylime \
        -it ${tpm12image}:${tpm12tag} >> ${container_id}
    docker exec -u 0 -it --tty "$(cat ${container_id})" \
        /bin/sh -c 'cd /root/keylime/test; chmod +x ./run_tests.sh; ./run_tests.sh -s openssl'
    docker stop "$(cat ${container_id})"
    docker rm "$(cat ${container_id})"
}

function tpm2 {
    container_id=$(mktemp)
    docker run --detach --privileged \
        -v $REPO:/root/keylime \
        -v /sys/fs/cgroup:/sys/fs/cgroup:ro \
        -it ${tpm20image}:${tpm20tag} >> ${container_id}
    docker exec -u 0 -it --tty "$(cat ${container_id})" \
        /bin/bash /root/keylime/.ci/test_wrapper.sh
    docker stop "$(cat ${container_id})"
    docker rm "$(cat ${container_id})"
}

while true; do
    echo -e ""
    read -p "Do you wish to test against TPM1.2(a) / TPM 2.0(b) or q(quit): " abq
    case $abq in
        [a]* ) tpm1;;
        [b]* ) tpm2;;
        [q]* ) exit;;
        * ) echo "Please answer 1, 2 q(quit)";;
    esac
done

Securing Keylime

Warning

This page is still under development and not complete. It will be so until this warning is removed.

System Hardening

TLS configuration

Reporting an issue

Please contact us directly at security@keylime.groups.io for any bug that might impact the security of this project. Do not use a github issue to report any potential security bugs.

Indices and tables