[
  {
    "path": ".gitignore",
    "content": "pkgs/\n"
  },
  {
    "path": ".gitlab-ci.yml",
    "content": "checks:shellcheck:\n  stage: checks\n  tags:\n    - docker\n  script:\n    - cd sbin/\n    - shellcheck -s bash * ../etc/anti-evil-maid.conf\n    - cd ../90anti-evil-maid/\n    - shellcheck -s bash anti-evil-maid-unseal ../sbin/anti-evil-maid-lib*\n\ninclude:\n- file: /r4.2/gitlab-base.yml\n  project: QubesOS/qubes-continuous-integration\n- file: /r4.2/gitlab-host.yml\n  project: QubesOS/qubes-continuous-integration\n- file: /r4.3/gitlab-base.yml\n  project: QubesOS/qubes-continuous-integration\n- file: /r4.3/gitlab-host.yml\n  project: QubesOS/qubes-continuous-integration\n"
  },
  {
    "path": ".qubesbuilder",
    "content": "host:\n  rpm:\n    build:\n    - anti-evil-maid.spec\n"
  },
  {
    "path": "90anti-evil-maid/anti-evil-maid-check-mount-devs",
    "content": "#!/bin/bash\n\n# shellcheck disable=SC1091\n. /lib/dracut-lib.sh\n\n# this cannot contain -u option because it causes an error inside\n# /lib/dracut-lib.sh\nset -eo pipefail\nshopt -s expand_aliases\n\nfunction check_device() {\n    local sysfs_path recursion_limit dm_name dm_target slave\n    sysfs_path=\"$1\"\n    recursion_limit=\"${2:-10}\"\n\n    if [ -r \"$sysfs_path/dm/name\" ]; then\n        dm_name=$(cat \"$sysfs_path\"/dm/name)\n        dm_target=$(dmsetup table \"$dm_name\" | cut -d ' ' -f 3)\n        # This also ensures that the dm table have only single entry\n        if [ \"$dm_target\" = \"crypt\" ]; then\n            return 0\n        elif [ -n \"$(ls -A \"$sysfs_path\"/slaves)\" ] && [ \"$recursion_limit\" -gt 0 ]; then\n            for slave in \"$sysfs_path\"/slaves/*; do\n                if ! check_device \"$slave\" \"$(( recursion_limit - 1 ))\"; then\n                    return 1\n                fi\n            done\n            return 0\n        else\n            return 1\n        fi\n    else\n        return 1\n    fi\n}\n\n\nroot_name=\"$(getarg root)\"\nif echo \"$root_name\" | grep -q = ; then\n    root_matches=$(blkid -t \"$root_name\" | wc -l)\n    if [ \"$root_matches\" -gt 1 ]; then\n        die \"AEM: multiple devices matching $root_name found, aborting!\"\n    fi\n    root_dev=$(blkid -o device -t \"$root_name\")\nelse\n    root_dev=$root_name\nfi\n\nroot_devid=$(lsblk -dnr -o MAJ:MIN \"$root_dev\")\n\nif ! check_device /sys/dev/block/\"$root_devid\"; then\n    die \"AEM: (bogus?) root device found not encrypted!\"\nfi\n\nfor lv in $(getarg rd.lvm.lv); do\n    if [ -e /dev/\"$lv\" ]; then\n        devid=$(lsblk -dnr -o MAJ:MIN /dev/\"$lv\")\n        if ! check_device /sys/dev/block/\"$devid\"; then\n            die \"AEM: (bogus?) device /dev/$lv found not encrypted!\"\n        fi\n    fi\ndone\n\nexit 0\n"
  },
  {
    "path": "90anti-evil-maid/anti-evil-maid-unseal",
    "content": "#!/bin/bash\nset -euo pipefail\nshopt -s expand_aliases\n\n# Anti Evil Maid for dracut by Invisible Things Lab\n# Copyright (C) 2010 Joanna Rutkowska <joanna@invisiblethingslab.com>\n#\n# Mount our device, read the sealed secret blobs, initialize TPM\n# and finally try to unseal the secrets and display them to the user\n\n\nMNT=/anti-evil-maid\nUNSEALED_SECRET=/tmp/unsealed-secret\nLUKS_HEADER_DUMP=/tmp/luks-header-dump\nLUKS_PCR=13\n\nPLYMOUTH_MESSAGES=()\n\nplymouth_message() {\n    if [ \"${#PLYMOUTH_MESSAGES[@]}\" -eq 0 ]; then\n        # add vertical \"padding\" to avoid printing messages over plymouth's\n        # prompt help\n        plymouth message --text=\"\"\n        plymouth message --text=\"\"\n        plymouth message --text=\"\"\n    fi\n\n    plymouth message --text=\"$*\"\n    PLYMOUTH_MESSAGES+=(\"$*\")\n}\n\nplymouth_messages_hide() {\n    for m in \"${PLYMOUTH_MESSAGES[@]}\"; do\n        plymouth hide-message --text=\"$m\"\n    done\n}\n\n# shellcheck source=../sbin/anti-evil-maid-lib\n. anti-evil-maid-lib\n\n\n# find AEM device\n\nUUID=$(getparams aem.uuid)\nDEV=/dev/disk/by-uuid/$UUID\n\nudevadm trigger\nudevadm settle\nwaitfor -b \"$DEV\"\n\nn=$(lsblk -nr -o UUID | grep -Fixc \"$UUID\") || true\nif [ \"$n\" != 1 ]; then\n    message \"Error: found ${n:-?} devices with UUID $UUID\"\n    exit 1\nfi\n\nLABEL=$(lsblk -dnr -o LABEL \"$DEV\")\nif [[ \"$LABEL\" != \"$LABEL_PREFIX\"* ]]; then\n    message \"AEM boot device $DEV has wrong label: $LABEL\"\n    exit 1\nfi\n\n\n# mount AEM device\n\nlog \"Mounting $DEV (\\\"$LABEL\\\")...\"\nmkdir -p \"$MNT\"\nmount -t ext4 -o ro \"$DEV\" \"$MNT\"\n# this way an error prior to unmounting will keep the device usable after initrd\ntrap 'umount \"$MNT\"' EXIT\n\n\n# setup TPM & copy secrets to initrd tmpfs\n\nlog \"Initializing TPM...\"\nmodprobe tpm_tis\nvalidatetpm || exit 1\nip link set dev lo up\nmkdir -p \"${TPMS_DIR%/*}\"\nlog \"Copying sealed AEM secrets...\"\ncp -Tr \"$MNT/aem/${TPMS_DIR##*/}\" \"${TPMS_DIR}\"\ntpmstartinitrdservices\n\nSEALED_SECRET_TXT=$TPM_DIR/$LABEL/secret.txt.sealed2\nSEALED_SECRET_KEY=$TPM_DIR/$LABEL/secret.key.sealed2\nSEALED_SECRET_OTP=$TPM_DIR/$LABEL/secret.otp.sealed2\nSEALED_SECRET_FRE=$TPM_DIR/$LABEL/secret.fre.sealed2\n\n\n# unmount AEM device\n\nlog \"Unmounting $DEV (\\\"$LABEL\\\")...\"\numount \"$MNT\"\n# remove umount trap set after mount\ntrap - EXIT\n\nif [ \"$(blockdev --getro \"$DEV\")\" = 1 ]; then\n    message \"You should now unplug the AEM device if it is intentionally read-only.\"\nfi\n\n\n# Extend PCR with LUKS header(s)\n\ngetluksuuids |\nsort -u |\nwhile read -r luksid; do\n    waitfor -b \"/dev/disk/by-uuid/$luksid\"\n\n    cryptsetup luksHeaderBackup \"/dev/disk/by-uuid/$luksid\" \\\n               --header-backup-file \"$LUKS_HEADER_DUMP\"\n    luks_header_hash=$(hashfile \"$LUKS_HEADER_DUMP\")\n    log \"Extending PCR $LUKS_PCR, value $luks_header_hash, device $luksid...\"\n    tpmpcrextend \"$LUKS_PCR\" \"$luks_header_hash\"\ndone\n\n\n# cache suffix and SRK password, if applicable\n\nmkdir -p \"$CACHE_DIR\"\necho \"${LABEL##\"$LABEL_PREFIX\"}\" >\"$SUFFIX_CACHE\"\n\nZ=$(tpmzsrk)\n\nif [ -n \"$Z\" ]; then\n    true >\"$SRK_PASSWORD_CACHE\"\nelse\n    for _ in 1 2 3; do\n        log \"Prompting for SRK password...\"\n\n        if systemd-ask-password --timeout=0 \\\n                                \"TPM SRK password to unseal the secret(s)\" \\\n                                > \"$SRK_PASSWORD_CACHE\" && checksrkpass; then\n             log \"Correct SRK password\"\n             break\n        fi\n\n        log \"Wrong SRK password\"\n    done\nfi\n\n\n# check freshness token\n\nlog \"Unsealing freshness token...\"\nif tpmunsealdata \"$Z\" \"$SEALED_SECRET_FRE\" \"$UNSEALED_SECRET\" \\\n                 \"$TPM_DIR/$LABEL\"; then\n    log \"Freshness token unsealed.\"\n    true >\"$CACHE_DIR/unseal-success\"\nelse\n    log \"Freshness token unsealing failed!\"\n    log \"This is expected during the first boot from a particular\"\n    log \"AEM media or after updating any of the boot components or\"\n    log \"changing their configuration.\"\n    exit 1\nfi\n\nif checkfreshness \"$UNSEALED_SECRET\"; then\n    log \"Freshness token valid, continuing.\"\nelse\n    log \"Freshness token invalid!\"\n    exit 1\nfi\n\n\n# unseal & show OTP if provisioned\n# unseal & decrypt key file unless the user switches to text secret mode\n\nif [ -e \"$SEALED_SECRET_OTP\" ]; then\n    alias otp=true\nelse\n    alias otp=false\nfi\n\nif otp; then\n    log \"Unsealing TOTP shared secret seed...\"\n    if tpmunsealdata \"$Z\" \"$SEALED_SECRET_OTP\" \"$UNSEALED_SECRET\" \\\n                     \"$TPM_DIR/$LABEL\"; then\n        log \"TOTP secret unsealed.\"\n\n        message \"\"\n        message \"Never type in your key file password unless the code below is correct!\"\n        message \"\"\n\n        seed=$(cat \"$UNSEALED_SECRET\")\n        last=\n        {\n            trap 'plymouth_messages_hide; exit' TERM\n            while :; do\n                now=$(date +%s)\n                if [ -z \"$last\" ] ||\n                   { [ \"$((now % 30))\" = 0 ] && [ \"$last\" != \"$now\" ]; }; then\n                    code=$(oathtool --totp -b \"$seed\")\n                    message \"[ $(date) ] TOTP code: $code\"\n                    last=$now\n                fi\n                sleep 0.1\n            done\n        } &\n        totp_loop_pid=$!\n\n        if tpmunsealdata \"$Z\" \"$SEALED_SECRET_KEY\" \"$UNSEALED_SECRET\" \\\n                         \"$TPM_DIR/$LABEL\"; then\n            for _ in 1 2 3; do\n                pass=$(systemd-ask-password --timeout=0 \\\n                       'LUKS key file password (or \"t\" to show text secret)')\n\n                if [ \"$pass\" = \"t\" ]; then\n                    alias otp=false\n                    break\n                fi\n\n                if scrypt dec -P \"$UNSEALED_SECRET\" /tmp/aem-keyfile \\\n                   <<<\"$pass\"; then\n                    log \"Correct LUKS key file password\"\n                    # dracut \"90crypt\" module will parse the\n                    #   rd.luks.key=/tmp/aem-keyfile\n                    # kernel cmdline arg and attempt to use it;\n                    # this file is deleted on root switch\n                    # along with everything in /tmp\n                    break\n                else\n                    log \"Wrong LUKS key file password\"\n                fi\n            done\n        fi\n\n        kill \"$totp_loop_pid\"\n    fi\nfi\n\n\n# unseal text secret\n\nif ! otp; then\n    log \"Unsealing text secret...\"\n    if tpmunsealdata \"$Z\" \"$SEALED_SECRET_TXT\" \"$UNSEALED_SECRET\" \\\n                     \"$TPM_DIR/$LABEL\"; then\n        {\n            message \"\"\n            message \"$(cat \"$UNSEALED_SECRET\" 2>/dev/null)\"\n            message \"\"\n        } 2>&1  # don't put the secret into the journal\n        message \"Never type in your disk password unless the secret above is correct!\"\n        waitforenter\n    fi\n\n    plymouth_messages_hide\n    clear\nfi\n\n\n# prevent sealing service from starting if user unplugged\n# the (supposedly read-only) AEM device\n\nif [ ! -b \"$DEV\" ]; then\n    rm -rf \"$CACHE_DIR\"\nfi\n"
  },
  {
    "path": "90anti-evil-maid/hosts",
    "content": "127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4\n::1         localhost localhost.localdomain localhost6 localhost6.localdomain6\n"
  },
  {
    "path": "90anti-evil-maid/module-setup.sh",
    "content": "#!/bin/bash\n\ncheck() {\n    which tpm_unsealdata tpm2_unseal  >/dev/null 2>&1 || return 1\n}\n\n\n#depends() {\n#}\n\n\ninstallkernel() {\n    instmods tpm_tis tpm_crb\n}\n\ninstall() {\n    inst_script \"$moddir\"/anti-evil-maid-unseal /sbin/anti-evil-maid-unseal\n    inst_script \"$moddir\"/anti-evil-maid-check-mount-devs /sbin/anti-evil-maid-check-mount-devs\n\n    inst $systemdsystemunitdir/cryptsetup-pre.target\n\n    dracut_install \\\n        /usr/sbin/anti-evil-maid-lib* \\\n        base32 \\\n        blockdev \\\n        clear \\\n        cryptsetup \\\n        cut \\\n        date \\\n        file \\\n        /usr/share/misc/magic \\\n        grep \\\n        head \\\n        install \\\n        killall \\\n        lsblk \\\n        oathtool \\\n        printf \\\n        scrypt \\\n        sed \\\n        seq \\\n        sha1sum \\\n        sort \\\n        tail \\\n        tcsd \\\n        trousers_changer_identify \\\n        tee \\\n        tpm_id \\\n        tpm2_id \\\n        tpm_nvinfo \\\n        tpm_nvread \\\n        tpm_nvread_stdout \\\n        tpm_pcr_extend \\\n        tpm_sealdata \\\n        tpm_unsealdata \\\n        tpm_z_srk \\\n        tpm2_z_srk \\\n        tr \\\n        uniq \\\n        wc \\\n        xargs \\\n        xxd\n\n    # TPM2-related:\n    # tpm2-tools\n    dracut_install \\\n        tpm2_changeauth \\\n        tpm2_create \\\n        tpm2_createprimary \\\n        tpm2_evictcontrol \\\n        tpm2_flushcontext \\\n        tpm2_load \\\n        tpm2_nvdefine \\\n        tpm2_nvread \\\n        tpm2_nvreadpublic \\\n        tpm2_nvundefine \\\n        tpm2_nvwrite \\\n        tpm2_nvwritelock \\\n        tpm2_pcrextend \\\n        tpm2_pcrread \\\n        tpm2_policycommandcode \\\n        tpm2_startauthsession \\\n        tpm2_unseal\n    # other utilities\n    dracut_install \\\n        mktemp \\\n        openssl \\\n        sha256sum\n    # such tpm2-tss libraries must be listed explicitly because they are\n    # discovered at runtime instead of being linked to during build\n    dracut_install \\\n        /usr/lib64/libtss2-tcti-device.so.0*\n\n    dracut_install \\\n        $systemdsystemunitdir/anti-evil-maid-unseal.service \\\n        $systemdsystemunitdir/anti-evil-maid-check-mount-devs.service \\\n        $systemdsystemunitdir/initrd.target.wants/anti-evil-maid-unseal.service \\\n        $systemdsystemunitdir/initrd.target.requires/anti-evil-maid-check-mount-devs.service\n\n    # all this crap below is needed for tcsd to start properly...\n    dracut_install ip\n    inst_simple \"$moddir\"/hosts /etc/hosts\n\n    touch \"$initdir/etc/\"{passwd,shadow,group}\n    chmod 0644 \"$initdir/etc/\"{passwd,group}\n    chmod 0640 \"$initdir/etc/shadow\"\n    for name in root tss; do\n        for file in /etc/{passwd,group}; do\n            if ! grep -q \"^$name:\" \"$initdir/$file\"; then\n                grep \"^$name:\" \"$file\" >> \"$initdir/$file\"\n            fi\n        done\n\n        if ! grep -q \"^$name:\" \"$initdir/etc/shadow\"; then\n            echo \"$name:*:::::::\" >> \"$initdir/etc/shadow\"\n        fi\n    done\n}\n"
  },
  {
    "path": "Makefile.builder",
    "content": "ifeq ($(PACKAGE_SET),dom0)\n    RPM_SPEC_FILES := anti-evil-maid.spec\nendif\n"
  },
  {
    "path": "README",
    "content": "Intro\n======\n\nAnti Evil Maid is an implementation of a TPM-based dynamic (Intel TXT) trusted\nboot for dracut/initramfs-based OSes (Fedora, Qubes, etc.) with a primary goal\nto prevent Evil Maid attacks.\n\nIn short, AEM relies on TPM and a feature found in Intel's vPro CPUs (TXT) to\ndetect tampering of various boot components.\n\nFor more information and discussion about potential attacks see:\n\nhttp://blog.invisiblethings.org/2011/09/07/anti-evil-maid.html\n(Note that this article is somewhat outdated, e.g. AEM uses Intel TXT now.)\n\nRequirements and security notes (\"before you start\")\n====================================================\n\n* Only TPM version 1.2 is currently supported. It may be possible to\n  configure your 2.0 TPM to emulate the 1.2 interface.\n\n* AEM is not compatible with (U)EFI boot. Legacy boot is required.\n\n* If you are using LUKS with LVM, you must encrypt the whole volume group\n  instead of each volume, or else AEM will fail to boot.\n\n* You MUST set a TPM owner password\n\n* Unless you're installing AEM to internal disk, TPM SRK password SHOULD\n  NOT be set (otherwise tboot will not be able to check whether critical\n  parts of RAM were not altered during S3 sleep). Please be aware that by\n  installing to internal disk and setting a TPM SRK password, your RAM\n  WILL NOT be protected against tampering when your laptop is suspended,\n  so make sure it is completely shut down any time an attacker might gain\n  physical access.\n\n* When RAM tampering is detected on wake-up from S3 sleep, the default\n  tboot policy only prints a warning into its log (`sudo txt-stat`) so\n  even if you checked it immediately after each wake-up, the attacker\n  might already have exfiltrated your login passphrase. Fix this by\n  either using two-factor for desktop login (weak) or create stronger\n  Launch Control Policy (LCP) and Verified Launch Policy (LCP) and write\n  them into TPM NVRAM -- especially make sure that tboot will forcefully\n  halt/reboot the platform if RAM tampering is detected. See tboot docs\n  for more information (`/usr/share/doc/tboot`). Creating a small NVRAM\n  area for tboot to write last error code might be a good idea for\n  debugging crashes on S3 suspend/resume:\n\n  `sudo tpmnv_defindex -i 0x20000002 -s 8 -pv 0 -rl 0x07 -wl 0x07 -p <ownerpw>`\n\n* Be aware that Intel TXT is vulnerable to System Management Mode (SMM)\n  exploits like overwriting System Management Interrupt (SMI) handlers.\n  Since SMM code is stored in platform firmware (BIOS) which usually is\n  updatable (and thus can be overwritten by an attacker), it is quite\n  an attractive target (and not just for the NSA). This can be fixed by\n  integrating an SMI Transfer Monitor (STM) into the platform (but this,\n  again, relies on the the same BIOS vendor who wrote a buggy SMM code\n  to safely implement STM). Additionally, STM does not appear to be\n  widely available yet (STM specification released mid-2015 by Intel).\n  You can check whether your platform includes STM:\n\n    `sudo txt-stat | grep -iA 1 stm`\n\n  Seeing \"stm: 0\" and \"stm_hash\" being all zeros means you DO NOT have\n  STM. Either way, BIOS is now part of your Trusted Computing Base (TCB)\n  and you need to prevent attackers with physical access from modifying\n  it. Good luck.\n\n  Some hints: connect the write protect pin on BIOS flash chip to ground\n  (prevents attacker from booting their own software which would bypass\n  BIOS protections and overwrite it) and make sure physically accessing\n  the chip will be tamper-evident by eg. covering the screws holding\n  laptop body together in glitter and taking high-res photos, then\n  examining before each use.\n\n* You might want to consider assessing the firmware security of your\n  platform using an automated tool such as CHIPSEC:\n\n    https://github.com/chipsec/chipsec\n\n* To recap -- you need to fully trust:\n  * CPU (Intel, since we're depending on TXT)\n    * sometimes over-optimizes for performance at the cost of security,\n      see eg. Meltdown/Spectre, cache attacks against SGX enclaves, ...\n  * TPM (various vendors)\n    * few known attacks sniffing and injecting commands on the LPC bus;\n      differential power analysis; buggy RSA key generation code\n    * note that any potential TPM exploits (should) have no means of\n      compromising your system directly -- a TPM under attacker's control\n      can only be used to hide the fact that a compromise has occurred\n      (ie. defeating the whole AEM feature)\n  * BIOS (a few vendors)\n    * it's full of holes!\n  * that the attacker cannot get physically inside your laptop without\n    you noticing (see the glitter hint above)\n\nUpgrading to AEM v4\n===================\n\nIf you have an existing AEM installation, there are a few steps required\nafter updating AEM packages to version >= 4.0 (available since Qubes R4).\n\nThe easiest way to upgrade is to completely reset the TPM and start from\nscratch and re-create all existing AEM devices.\n\nShould you want to migrate without resetting the TPM (in case you're using\nit for something else besides Qubes AEM), you can manually replicate the\nsteps taken in the TPM setup script (/usr/sbin/anti-evil-maid-tpm-setup).\nNote that you still need to re-create all provisioned AEM media afterwards.\n\nOtherwise, perform a TPM reset (via BIOS) and skip to the \"Installation\"\nsection below.\n\nInstallation\n=============\n\nThe instructions below assume Qubes OS.\n\n1) Enable TPM in BIOS. Also enable TXT if there is an option for it.\n\n2) Install and Verify TPM support under your OS/Dom0.\n\na) Install anti-evil-maid packages (in Dom0 on Qubes). It will install all the\nrequired dependencies and tools.\n\n# qubes-dom0-update anti-evil-maid\n\nb) Verify kernel support for TPM:\n\n# cat /sys/class/tpm/tpm0/pcrs\n\nIf you see something like this:\n\nPCR-00: 67 DC B4 8C AB 8D C7 9B 28 84 D9 15 69 DE 82 F2 F0 E1 2A D8\nPCR-01: 11 75 9A 19 E5 BD E8 4E DA 1C 01 EC 53 87 FD 50 18 E1 94 1E\nPCR-02: 4B 43 98 82 65 04 E9 F4 14 78 26 F9 ED EA 92 91 6D FD AF D5\nPCR-03: B2 A8 3B 0E BF 2F 83 74 29 9A 5B 2B DF C3 1E A9 55 AD 72 36\nPCR-04: 93 33 4E 81 A6 9C 80 54 D6 87 C7 FD 76 7C 6F 4C 70 FC C6 73\n(...)\n\n... then your TPM is supported by your kernel.\n\nIf your tpm has already been owned in the past, you can reset it by running\ntpm_clear -z, powering your computer off, and then resetting TPM in the BIOS\n(e.g.: TPM Authentication Reset).\n\nc) Initialize the TPM for use with AEM\n\n# anti-evil-maid-tpm-setup -z\n\nIn case you want to install AEM to an internal disk, an SRK password must\nbe set up in order for AEM to be secure. The SRK password can be set up\nby NOT passing the \"-z\" option to the above command. Should you not\nanticipate future need for internal AEM boot device and want to use\nexternal media only, use the \"-z\" option. If you later decide to provision\nAEM on the internal drive, create an SRK password first:\n\n# tpm_changeownerauth -s\n\nYou will need to copy & paste the randomly-generated TPM owner password\nfrom the /var/lib/anti-evil-maid/tpm-owner-pw file. Existing AEM media\nwill _not_ need to be re-sealed.\n\n3) Setup Anti Evil Maid\n\na) SINIT module\n\nYou should download the SINIT module required for your system.\n\nIntel documented the required SINIT module depending on your CPU platform in:\nhttp://software.intel.com/en-us/articles/intel-trusted-execution-technology\nBut DO NOT download the modules besides the SINIT/RACM from here. The download links provide old versions and broken binaries.\n\nYou can then download the module and unzip it. All the modules could be\ndownloaded from:\n\nhttps://cdrdv2.intel.com/v1/dl/getContent/630744?wapkw=intel%20txt%20sinit%20acm%20revocation%20\n\nFind the module fitting to your platform and rename it to the names mentioned on the intel website link.\n\nAlso, make sure you have the latest RACM update, if available (2nd & 3rd gen):\nhttps://software.intel.com/system/files/article/183305/intel-txt-sinit-acm-revocation-tools-guide-rev1-0_2.pdf\n\nIt's possible to use 3rd gen SINIT/RACM on 2nd gen platforms. In fact, the\nonly RACM available at the time of writing is for the 3rd gen, while the 2nd\ngen platforms were also affected by the buffer overflow bug in old SINIT\nversion.\n\nFinally, you should retrieve the BIN file inside /boot in dom0. E.g., run from\ndom0:\n\n$ sudo -s\n# qvm-run --pass-io vm_name_containing_bin_file 'cat /home/user/path_to_sinit/name_of_sinit_file.BIN' > /boot/name_of_sinit_file.BIN\n\nNOTE: The SINIT files are digitally signed by Intel. While there is no easy\nway to verify their integrity after downloading (and after copying to Dom0),\nstill, the operation of placing such a file into Dom0's /boot filesystem\nshould be reasonably safe to do -- after all the file should not be processed\nby any software in Dom0, and only by the SENTER instruction of the processes,\nwhich, we hope, correctly verifies the signature before executing it...\n\nb) Create an Anti Evil Maid device:\n\n# anti-evil-maid-install -h\n\nPlease note that each AEM device you provision should have a unique\nfilesystem label suffix (use the '-s' option). You may safely re-use\nsuffixes for destroyed devices.\n\nInstallation directly onto a (truly) read-only media (such as a CD-R) is not\nsupported. You can, however, copy the contents of an existing RW media onto\nRO media after the initial sealing takes place. Physical write-protect\nswitches on USB sticks are fine (install AEM in RW mode, then\nflip the switch and proceed to use as RO media). Remember to always pull\nout the RO media when your text secret or TOTP code is displayed! Failing\nto do that will result in invalidation of freshness token in the TPM memory\nand the AEM media will fail to perform verified boot next time, falling\nback to non-AEM-protected mode.\n\nFor example, to install on the internal boot partition (assuming that it\nis /dev/sda1):\n\n# anti-evil-maid-install /dev/sda1\n\nOr better, create an external AEM boot device (in this case an SD card):\n\n# anti-evil-maid-install /dev/mmcblk0p1\n\nAlternatively, a multi-factor authentication AEM boot device can be created,\nwhich provides additional protection against shoulder surfing and video\nsurveillance -- with the above setups, if an attacker sees your disk\nencryption password as you're typing it then they can simply steal and decrypt\nyour computer. Long story short, if you ever need to boot your AEM-protected\ncomputer in public or anywhere a camera may be hidden, this is the thing you\nwant to use. However, this setup requires an external boot media (eg. USB\nstick or memory card) for maximum protection and owning a suitable two-factor\nauthentication device supporting time-based one-time passwords (TOTP). There\nare apps available for Android/Apple smartphones (Google Authenticator,\nFreeOTP Authenticator) and Windows Phone (Microsoft Authenticator). You can\nalso use a dedicated hardware token, either a reseedable one (letting the AEM\ninstaller generate a seed for you), or a manufacturer-seeded token (you will\nneed to store the seed in /var/lib/anti-evil-maid/aem<suffix>/secret.otp in\nbase32 format, then run the AEM installer). The command to set this all up is\nsimple:\n\n# anti-evil-maid-install -m /dev/sdb1\n\nThis will automatically generate a TOTP seed and display it as a QR code for\nyou to enroll on your 2FA device (if it doesn't have a camera, there's also\ntext version for manual entry). Once a successful TOTP seed enrollment is\nverified (you need to enter the 6-digit code displayed on your 2FA device),\na LUKS key file will be randomly generated and encrypted by a password of\nyour choice (make sure you're not using this password for anything else).\nThis key file will then get added to your encrypted drive's LUKS key slot.\nFor more details, see the associated qubes-devel mailing list thread:\n  https://groups.google.com/d/topic/qubes-devel/8cAjSyg1msM/discussion\n\nIn case you would like to install multi-factor AEM on internal disk, beware\nthat keyboard observation attacks cannot be prevented! Plus, you still need\nto have TPM SRK password set. The only advantage over plain static text secret\nis, of course, that there's not static secret **shown on the screen** to\nobserve (ie. cover the keyboard while typing AEM passwords).\n\nIf you've chosen to install AEM on an external device (and not the internal\ndrive), you should then remove the internal boot partition from dom0's\n/etc/fstab, never mount it again in dom0, and never boot from it again,\nbecause an attacker might modify it to exploit GRUB or dom0 filesystem\ndrivers.\n\nNote: If you choose to use a USB device (e.g., a flash drive) as your AEM\ndevice and you previously created a USB qube, then you may have to unhide\nyour USB controller from dom0:\n\n  1. Open the file `/etc/default/grub` in dom0.\n  2. Find the line that begins with `GRUB_CMDLINE_LINUX`.\n  3. If present, remove `rd.qubes.hide_all_usb` from that line.\n  4. Save and close the file.\n  5. Run the command `grub2-mkconfig -o /boot/grub2/grub.cfg` in dom0.\n  6. Reboot.\n\nc) Create a secret text (note: it cannot be larger than 255 bytes):\n\nNote: This step is unnecessary if using the multi-factor auth setup, but\ncan serve as a fallback option in case you ever find yourself temporarily\nnot having access to your 2FA device (eg. smartphone or hardware TOTP token).\n\n# cat >/var/lib/anti-evil-maid/aem/secret.txt <<END\nMy secret text\nhas two lines\nEND\n\nNote: You are saving this not-yet-sealed secret to your root\nfilesystem without further encryption (besides that provided by LUKS).\nIf an attacker somehow gains access to your decrypted filesystem data,\ne.g. by compelling you to reveal the LUKS passphrase, they can of\ncourse see these secrets. So they are probably not the right place to\nstore your most intimate confessions. ;)\n\n4) Reboot the system, choose one of the entries called \"AEM Qubes\". This will\nattempt to perform a \"measured launch\" using tboot and the SINIT module you\ndownloaded, which records the Xen, kernel, and initrd versions used in PCRs\n17-19 of the TPM for use in sealing and unsealing your secret. If the measured\nlaunch fails for any reason, tboot will fall back to a normal boot and AEM\nwill not function.\n\na) Enter your SRK password if prompted. You won't see your secret afterwards,\nbecause it hasn't been sealed yet (seeing a `Freshness token unsealing\nfailed!` message here is expected). Enter your disk decryption passphrase\nanyway, right now you still trust your system.\n\nAs the system continues booting, AEM will automatically seal your\nsecret(s). You should see a line, or multiple lines, like this one:\n\nSealed /var/lib/anti-evil-maid/aem/secret.txt using\n --pcr 13 --pcr 17 --pcr 18 --pcr 19\n\nDebug output can be read using:\n\n$ journalctl -u anti-evil-maid-unseal -u anti-evil-maid-seal\n\nNote: The PCRs used to seal to can be changed in /etc/anti-evil-maid.conf\n-- though the defaults should work just fine. If you decide to change\nthem and you want to reseal immediately, run anti-evil-maid-seal manually\nonce.\n\nIf you get a message that the \"PCR sanity check failed\" and you are sure you\nhave saved the right SINIT blob in step 3.a, then check the tboot log for\ndetails. The easiest way to view it is to set \"logging=vga vga_delay=10\" on\nthe \"multiboot /tboot.gz\" line in grub.cfg and reboot. Alternatively, run\n`sudo txt-stat` from dom0. For more information, see the tboot readme\n(/usr/share/doc/tboot/README on an installed system).\n\nb) If a chunk of your installed RAM seems to be missing after the reboot\n(which can be checked by running \"xl info\" in dom0), do the following:\n\n# echo 'export GRUB_CMDLINE_TBOOT=min_ram=0x2000000' >>/etc/default/grub\n# grub2-mkconfig -o /boot/grub2/grub.cfg\n\nThen go to step 4.a again. A discussion of this problem can be found at\nhttp://thread.gmane.org/gmane.comp.boot-loaders.tboot.devel/610/focus=611\nand by searching for \"min_ram\" in the qubes mailing lists.\n\nc) Now, every time you boot your system (from your Anti Evil Maid stick)\nyou should see your secret text or TOTP code displayed *before* you\nenter your LUKS disk encryption or key file passphrase.\n\nXen/kernel/BIOS/firmware upgrades\n==================================\n\nAfter Xen, kernel, BIOS, or firmware upgrades, you will need to reboot\nand enter your disk decryption passphrase even though you can't see your\nsecret. Please note that you will see a `Freshness toekn unsealing failed!`\nerror. It (along with your AEM secrets) will be resealed again automatically\nlater in the boot process (see step 4.a).\n\nSome additional things that can cause AEM secrets and freshness token to\nfail to unseal (non-exhaustive list):\n\n* changing the LUKS header of the encrypted root partition\n* modifying the initrd (adding/removing files or just re-generating it)\n* changing kernel commandline parameters in GRUB\n\n\nWhat to do in case of compromise\n================================\n\nFor a discussion of potential attacks against Anti Evil Maid, see the article\nreferenced at the beginning.\n\n\nAEM media copied/stolen by an attacker\n--------------------------------------\n\nIf you have your system up and running or have an extra AEM media using which\nyou can boot the system before the attacker can get to it:\n\n* `sudo -s` in dom0\n* `. /usr/sbin/anti-evil-maid-lib` (note the dot at the beginning)\n* `revokefreshness <suffix>` (where `<suffix>` is the missing label suffix)\n\nIn case you do not remember the used media label suffix, take a look at\n`/var/lib/anti-evil-maid/`. If the particular media had no special suffix set\n(i.e. if the subdirectory is just named `aem`), use `revokefreshness \"\"`.\n\nAlternatively, `resetfreshness` will wipe all freshness tokens from the TPM\n(thus invalidating all enrolled AEM media and forcing you to boot an\nunverified system).\n\nAs a last resort, you can attempt to reset the TPM from BIOS (with similar\neffect to `resetfreshness`). Otherwise, it's game over.\n\n\nSomeone saw my LUKS passphrase\n------------------------------\n\nYou should've installed AEM in multi-factor mode.\n\nIf you're fairly confident the attacker does not possess a bitwise copy\nof your encrypted Qubes OS drive, change the LUKS passphrase:\n\n* determine the path to LUKS-encrypted disk (usually /dev/sda2)\n* add a new password with `sudo cryptsetup luksAddKey <disk>`\n* remove old one with `sudo cryptsetup luksRemoveKey <disk>`\n\nAs these actions will change the LUKS header, which is fed into one\nof the TPM PCRs upon each AEM boot, all the existing AEM media will\nget invalidated (ie. fall back to unverified boot).\n\nBeware that solid-state devices will most likely NOT overwrite the\nLUKS header, but rather write the new one into another memory cell\n(due to wear leveling algorithms designed to prolong SSD life).\nIf you're worried about this and have recent-enough backups (as\nyou always should), perform an ATA secure erase of the whole SSD\nusing a live CD and then reinstall Qubes OS.\nhttps://ata.wiki.kernel.org/index.php/ATA_Secure_Erase\n\n\nSomeone saw my text secret\n--------------------------\n\nAgain, if you're going to boot your Qubes OS in public places,\nusing multi-factor AEM is strongly recommended.\n\nAssuming the attacker haven't yet had access to your computer\n(in order to install a compromised bootloader which will show\nthe correct secret but record your LUKS passphrase), simply\nchanging the text secret and re-creating affected AEM media\n(if you used same secret for multiple ones) will do.\n\nThe text secrets are stored in\n`/var/lib/anti-evil-maid/aem<suffix>/secret.txt`\n\nMake sure to never trust the old text secret ever again!\n\n\n\nTODO: write up more scenarios and how to recover, best practices\n"
  },
  {
    "path": "anti-evil-maid.spec.in",
    "content": "Name:\t\tanti-evil-maid\nVersion:\t@VERSION@\nRelease:\t1%{?dist}\nSummary:    \tAnti Evil Maid for initramfs-based systems.\nRequires:\tdracut grub2-tools parted tboot tpm-tools\nRequires:\ttpm-extra >= 4.0.0\nRequires:\ttrousers-changer >= 4.0.0\nRequires:\tsystemd >= 227\nRequires:\tcoreutils >= 8.25-2\nRequires:\tscrypt qrencode oathtool\nRequires:\ttpm2-tools openssl\nRequires(post):\tdracut grub2-tools tboot systemd\nObsoletes:\tanti-evil-maid-dracut\nVendor:\t\tInvisible Things Lab\nLicense:\tGPL\nURL:\t\thttp://www.qubes-os.org\nSource0:\t%{name}-%{version}.tar.gz\nBuildArch: noarch\n\n%description\nAnti Evil Maid for initramfs-based systems.\n\n%prep\n%setup -q\n\n%install\n\nmkdir -p $RPM_BUILD_ROOT/usr\ncp -r sbin $RPM_BUILD_ROOT/usr\n\nmkdir -p $RPM_BUILD_ROOT/usr/share/doc/anti-evil-maid\ncp README $RPM_BUILD_ROOT/usr/share/doc/anti-evil-maid\n\ncp -r etc $RPM_BUILD_ROOT\n\nmkdir -p $RPM_BUILD_ROOT/mnt/anti-evil-maid\nmkdir -p $RPM_BUILD_ROOT/var/lib/anti-evil-maid\n\nmkdir -p $RPM_BUILD_ROOT/usr/lib/dracut/modules.d\ncp -r 90anti-evil-maid $RPM_BUILD_ROOT/usr/lib/dracut/modules.d/\n\nmkdir -p $RPM_BUILD_ROOT/usr/lib\ncp -r systemd $RPM_BUILD_ROOT/usr/lib\n\n%files\n/usr/sbin/anti-evil-maid-install\n/usr/sbin/anti-evil-maid-lib\n/usr/sbin/anti-evil-maid-lib-tpm1\n/usr/sbin/anti-evil-maid-lib-tpm2\n/usr/sbin/anti-evil-maid-seal\n/usr/sbin/anti-evil-maid-tpm-setup\n/usr/share/doc/anti-evil-maid/README\n/usr/lib/systemd/system/anti-evil-maid-seal.service\n/usr/lib/systemd/system/tcsd.service.d/anti-evil-maid-seal.conf\n/usr/lib/systemd/system/basic.target.wants/anti-evil-maid-seal.service\n/etc/anti-evil-maid.conf\n/etc/grub.d/19_linux_xen_tboot\n%dir /mnt/anti-evil-maid\n%dir /var/lib/anti-evil-maid\n\n/etc/dracut.conf.d/anti-evil-maid.conf\n/usr/lib/dracut/modules.d/90anti-evil-maid\n/usr/lib/systemd/system/anti-evil-maid-unseal.service\n/usr/lib/systemd/system/anti-evil-maid-check-mount-devs.service\n/usr/lib/systemd/system/initrd.target.wants/anti-evil-maid-unseal.service\n/usr/lib/systemd/system/initrd.target.requires/anti-evil-maid-check-mount-devs.service\n\n%define tboot_grub /etc/grub.d/20_linux_tboot /etc/grub.d/20_linux_xen_tboot\n\n%define refresh \\\ndracut --regenerate-all --force \\\ngrub2-mkconfig -o /boot/grub2/grub.cfg \\\nsystemctl daemon-reload\n\n%post\nchmod -x %tboot_grub\n%refresh\n\n%postun\nif [ \"$1\" = 0 ]; then\n    %refresh\n    chmod -f +x %tboot_grub || true\nfi\n\n%triggerin -- tboot\nchmod -x %tboot_grub\n\n%changelog\n@CHANGELOG@\n"
  },
  {
    "path": "etc/anti-evil-maid.conf",
    "content": "# List of PCRs -- but note that Qubes DOESN'T USE TrustedGRUB:\n#\n#   0-3: (SRTM) BIOS, option ROMs, platform config\n#     4: (SRTM) MBR\n#   5-7: (SRTM) OEM specific, probably safe to skip\n#   8,9: (SRTM) TrustedGRUB1 stage2\n#    12: (SRTM) Xen/kernel params passed by TrustedGRUB1\n#    13:        LUKS header(s)\n#    14: (SRTM) Xen/kernel/initrd loaded by TrustedGRUB1\n# 17-19: (DRTM) TBoot\n#\n# SRTM =  Static Root of Trust Measurement\n# DRTM = Dynamic Root of Trust Measurement (Intel TXT)\n\n# shellcheck disable=SC2034\nSEAL=\"--pcr 13 --pcr 17 --pcr 18 --pcr 19\"\n"
  },
  {
    "path": "etc/dracut.conf.d/anti-evil-maid.conf",
    "content": "add_dracutmodules+=\" anti-evil-maid \"\n"
  },
  {
    "path": "etc/grub.d/19_linux_xen_tboot",
    "content": "#! /bin/sh\nset -e\n\n# grub-mkconfig helper script.\n# Copyright (C) 2006,2007,2008,2009,2010  Free Software Foundation, Inc.\n#\n# GRUB is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# GRUB is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with GRUB.  If not, see <http://www.gnu.org/licenses/>.\n\nprefix=\"/usr\"\nexec_prefix=\"/usr\"\ndatarootdir=\"${prefix}/share\"\n\n. \"/usr/share/grub/grub-mkconfig_lib\"\n\nexport TEXTDOMAIN=grub\nexport TEXTDOMAINDIR=\"${datarootdir}/locale\"\n\nCLASS=\"--class gnu-linux --class gnu --class os --class xen\"\n\nif [ \"x${GRUB_DISTRIBUTOR}\" = \"x\" ] ; then\n  OS=\"$(sed 's, release .*$,,g' /etc/system-release)\"\nelse\n  OS=\"${GRUB_DISTRIBUTOR}\"\n  CLASS=\"--class $(echo ${GRUB_DISTRIBUTOR} | tr 'A-Z' 'a-z' | cut -d' ' -f1) ${CLASS}\"\nfi\n\n# loop-AES arranges things so that /dev/loop/X can be our root device, but\n# the initrds that Linux uses don't like that.\ncase ${GRUB_DEVICE} in\n  /dev/loop/*|/dev/loop[0-9])\n    GRUB_DEVICE=`losetup ${GRUB_DEVICE} | sed -e \"s/^[^(]*(\\([^)]\\+\\)).*/\\1/\"`\n  ;;\nesac\n\nif [ \"x${GRUB_DEVICE_UUID}\" = \"x\" ] || [ \"x${GRUB_DISABLE_LINUX_UUID}\" = \"xtrue\" ] \\\n    || ! test -e \"/dev/disk/by-uuid/${GRUB_DEVICE_UUID}\" \\\n    || uses_abstraction \"${GRUB_DEVICE}\" lvm; then\n  LINUX_ROOT_DEVICE=${GRUB_DEVICE}\nelse\n  LINUX_ROOT_DEVICE=UUID=${GRUB_DEVICE_UUID}\nfi\n\n# Allow overriding GRUB_CMDLINE_LINUX and GRUB_CMDLINE_LINUX_DEFAULT.\nif [ \"${GRUB_CMDLINE_LINUX_XEN_REPLACE}\" ]; then\n  GRUB_CMDLINE_LINUX=\"${GRUB_CMDLINE_LINUX_XEN_REPLACE}\"\nfi\nif [ \"${GRUB_CMDLINE_LINUX_XEN_REPLACE_DEFAULT}\" ]; then\n  GRUB_CMDLINE_LINUX_DEFAULT=\"${GRUB_CMDLINE_LINUX_XEN_REPLACE_DEFAULT}\"\nfi\n\nGRUBFS=\"`${grub_probe} --device ${GRUB_DEVICE} --target=fs 2>/dev/null || true`\"\n\nif [ x\"$GRUBFS\" = x ]; then\n    GRUBFS=\"$(stat -f --printf=%T /)\"\nfi\n\ncase x\"$GRUBFS\" in\n    xbtrfs)\n\trootsubvol=\"`make_system_path_relative_to_its_root /`\"\n\trootsubvol=\"${rootsubvol#/}\"\n\tif [ \"x${rootsubvol}\" != x ]; then\n\t    GRUB_CMDLINE_LINUX=\"rootflags=subvol=${rootsubvol} ${GRUB_CMDLINE_LINUX}\"\n\tfi;;\n    xzfs)\n\trpool=`${grub_probe} --device ${GRUB_DEVICE} --target=fs_label 2>/dev/null || true`\n\tbootfs=\"`make_system_path_relative_to_its_root / | sed -e \"s,@$,,\"`\"\n\tLINUX_ROOT_DEVICE=\"ZFS=${rpool}${bootfs}\"\n\t;;\nesac\n\ntitle_correction_code=\n\nlinux_entry ()\n{\n  os=\"$1\"\n  version=\"$2\"\n  xen_version=\"$3\"\n  type=\"$4\"\n  args=\"$5\"\n  xen_args=\"$6\"\n  if [ -z \"$boot_device_id\" ]; then\n      boot_device_id=\"$(grub_get_device_id \"${GRUB_DEVICE}\")\"\n  fi\n  if [ x$type != xsimple ] ; then\n      if [ x$type = xrecovery ] ; then\n\t  title=\"$(gettext_printf \"AEM %s boot, with Xen %s and Linux %s (recovery mode)\" \"${os}\" \"${xen_version}\" \"${version}\")\"\n      else\n\t  title=\"$(gettext_printf \"AEM %s boot, with Xen %s and Linux %s\" \"${os}\" \"${xen_version}\" \"${version}\")\"\n      fi\n      replacement_title=\"$(echo \"Advanced options with AEM boot for ${OS}\" | sed 's,>,>>,g')>$(echo \"$title\" | sed 's,>,>>,g')\"\n      if [ x\"Xen ${xen_version}>$title\" = x\"$GRUB_ACTUAL_DEFAULT\" ]; then\n         quoted=\"$(echo \"$GRUB_ACTUAL_DEFAULT\" | grub_quote)\"\n         title_correction_code=\"${title_correction_code}if [ \\\"x\\$default\\\" = '$quoted' ]; then default='$(echo \"$replacement_title\" | grub_quote)'; fi;\"\n         grub_warn \"$(gettext_printf \"Please don't use old title \\`%s' for GRUB_DEFAULT, use \\`%s' (for versions before 2.00) or \\`%s' (for 2.00 or later)\" \"$GRUB_ACTUAL_DEFAULT\" \"$replacement_title\" \"gnulinux-advanced-$boot_device_id>gnulinux-$version-$type-$boot_device_id\")\"\n      fi\n      echo \"menuentry '$(echo \"$title\" | grub_quote)' ${CLASS} \\$menuentry_id_option 'xen-gnulinux-$version-$type-$boot_device_id' {\" | sed \"s/^/$submenu_indentation/\"\n  else\n      title=\"$(gettext_printf \"AEM %s, with Xen hypervisor\" \"${os}\")\"\n      echo \"menuentry '$(echo \"$title\" | grub_quote)' ${CLASS} \\$menuentry_id_option 'xen-gnulinux-simple-$boot_device_id' {\" | sed \"s/^/$submenu_indentation/\"\n  fi\n  if [ x$type != xrecovery ] ; then\n      save_default_entry | grub_add_tab | sed \"s/^/$submenu_indentation/\"\n  fi\n\n  if [ -z \"${prepare_boot_cache}\" ]; then\n    prepare_boot_cache=\"$(prepare_grub_to_access_device ${GRUB_DEVICE_BOOT} | grub_add_tab)\"\n  fi\n  printf '%s\\n' \"${prepare_boot_cache}\" | sed \"s/^/$submenu_indentation/\"\n  tmessage=\"$(gettext_printf \"Loading tboot ...\")\"\n  xmessage=\"$(gettext_printf \"Loading Xen %s ...\" ${xen_version})\"\n  lmessage=\"$(gettext_printf \"Loading Linux %s ...\" ${version})\"\n  sed \"s/^/$submenu_indentation/\" << EOF\n\techo\t'$(echo \"$tmessage\" | grub_quote)'\n\tmultiboot\t/tboot.gz placeholder logging=memory,serial ${GRUB_CMDLINE_TBOOT}\n\techo\t'$(echo \"$xmessage\" | grub_quote)'\n        if [ \"\\$grub_platform\" = \"pc\" -o \"\\$grub_platform\" = \"\" ]; then\n            xen_rm_opts=\n        else\n            xen_rm_opts=\"no-real-mode edd=off\"\n        fi\n\tmodule\t${rel_xen_dirname}/${xen_basename} placeholder ${xen_args} \\${xen_rm_opts}\n\techo\t'$(echo \"$lmessage\" | grub_quote)'\n\tmodule\t${rel_dirname}/${basename} placeholder root=${linux_root_device_thisversion} ro ${args} aem.uuid=${GRUB_DEVICE_BOOT_UUID} rd.luks.key=/tmp/aem-keyfile rd.luks.crypttab=no\nEOF\n  if test -n \"${initrd}\" ; then\n    # TRANSLATORS: ramdisk isn't identifier. Should be translated.\n    message=\"$(gettext_printf \"Loading initial ramdisk ...\")\"\n    sed \"s/^/$submenu_indentation/\" << EOF\n\techo\t'$(echo \"$message\" | grub_quote)'\n\tmodule\t${rel_dirname}/${initrd}\nEOF\n  fi\n  if test -n \"${sinit_module_list}\" ; then\n    for i in ${sinit_module_list} ; do\n      sinit_module=`basename $i`\n      message=\"$(gettext_printf \"Loading SINIT module %s ...\" ${sinit_module})\"\n      sed \"s/^/$submenu_indentation/\" << EOF\n\techo\t'$message'\n\tmodule\t/${sinit_module}\nEOF\n    done\n  fi\n  sed \"s/^/$submenu_indentation/\" << EOF\n}\nEOF\n}\n\nlinux_list=`for i in /boot/vmlinu[xz]-* /vmlinu[xz]-* /boot/kernel-*; do\n    if grub_file_is_not_garbage \"$i\"; then\n    \tbasename=$(basename $i)\n\tversion=$(echo $basename | sed -e \"s,^[^0-9]*-,,g\")\n\tdirname=$(dirname $i)\n\tconfig=\n\tfor j in \"${dirname}/config-${version}\" \"${dirname}/config-${alt_version}\" \"/etc/kernels/kernel-config-${version}\" ; do\n\t    if test -e \"${j}\" ; then\n\t\tconfig=\"${j}\"\n\t\tbreak\n\t    fi\n\tdone\n        if (grep -qx \"CONFIG_XEN_DOM0=y\" \"${config}\" 2> /dev/null || grep -qx \"CONFIG_XEN_PRIVILEGED_GUEST=y\" \"${config}\" 2> /dev/null); then echo -n \"$i \" ; fi\n    fi\n    done`\nif [ \"x${linux_list}\" = \"x\" ] ; then\n    exit 0\nfi\n\nfile_is_not_sym () {\n    case \"$1\" in\n\t*/xen-syms-*)\n\t    return 1;;\n\t*)\n\t    return 0;;\n    esac\n}\n\nxen_list=`for i in /boot/xen*; do\n        if grub_file_is_not_garbage \"$i\" && file_is_not_sym \"$i\" ; then echo -n \"$i \" ; fi\n      done`\n\nsinit_module_list=`for i in /boot/*SINIT*.BIN; do\n  if grub_file_is_not_garbage \"$i\"; then\n    echo \"$i\"\n  fi\ndone`\n\nprepare_boot_cache=\nboot_device_id=\n\ntitle_correction_code=\n\nmachine=`uname -m`\n\ncase \"$machine\" in\n    i?86) GENKERNEL_ARCH=\"x86\" ;;\n    mips|mips64) GENKERNEL_ARCH=\"mips\" ;;\n    mipsel|mips64el) GENKERNEL_ARCH=\"mipsel\" ;;\n    arm*) GENKERNEL_ARCH=\"arm\" ;;\n    *) GENKERNEL_ARCH=\"$machine\" ;;\nesac\n\necho \"if [ -d /aem/ ]; then\"\n\n# Extra indentation to add to menu entries in a submenu. We're not in a submenu\n# yet, so it's empty. In a submenu it will be equal to '\\t' (one tab).\nsubmenu_indentation=\"\"\n\nis_first_entry=true\n\nwhile [ \"x${xen_list}\" != \"x\" ] ; do\n    list=\"${linux_list}\"\n    current_xen=`version_find_latest $xen_list`\n    xen_basename=`basename ${current_xen}`\n    xen_dirname=`dirname ${current_xen}`\n    rel_xen_dirname=`make_system_path_relative_to_its_root $xen_dirname`\n    xen_version=`echo $xen_basename | sed -e \"s,.gz$,,g;s,^xen-,,g\"`\n    if [ -z \"$boot_device_id\" ]; then\n\tboot_device_id=\"$(grub_get_device_id \"${GRUB_DEVICE}\")\"\n    fi\n    if [ \"x$is_first_entry\" != xtrue ]; then\n\techo \"\tsubmenu '$(gettext_printf \"Xen hypervisor, version %s with AEM boot\" \"${xen_version}\" | grub_quote)' \\$menuentry_id_option 'xen-hypervisor-$xen_version-$boot_device_id' {\"\n    fi\n    while [ \"x$list\" != \"x\" ] ; do\n\tlinux=`version_find_latest $list`\n\tgettext_printf \"Found linux image: %s\\n\" \"$linux\" >&2\n\tbasename=`basename $linux`\n\tdirname=`dirname $linux`\n\trel_dirname=`make_system_path_relative_to_its_root $dirname`\n\tversion=`echo $basename | sed -e \"s,^[^0-9]*-,,g\"`\n\talt_version=`echo $version | sed -e \"s,\\.old$,,g\"`\n\tlinux_root_device_thisversion=\"${LINUX_ROOT_DEVICE}\"\n\n\tinitrd=\n\tfor i in \"initrd.img-${version}\" \"initrd-${version}.img\" \"initrd-${version}.gz\" \\\n\t   \"initrd-${version}\" \"initramfs-${version}.img\" \\\n\t   \"initrd.img-${alt_version}\" \"initrd-${alt_version}.img\" \\\n\t   \"initrd-${alt_version}\" \"initramfs-${alt_version}.img\" \\\n\t   \"initramfs-genkernel-${version}\" \\\n\t   \"initramfs-genkernel-${alt_version}\" \\\n\t   \"initramfs-genkernel-${GENKERNEL_ARCH}-${version}\" \\\n\t   \"initramfs-genkernel-${GENKERNEL_ARCH}-${alt_version}\" ; do\n\t    if test -e \"${dirname}/${i}\" ; then\n\t\tinitrd=\"$i\"\n\t\tbreak\n\t    fi\n\tdone\n\tif test -n \"${initrd}\" ; then\n\t    gettext_printf \"Found initrd image: %s\\n\" \"${dirname}/${initrd}\" >&2\n\telse\n    # \"UUID=\" magic is parsed by initrds.  Since there's no initrd, it can't work here.\n\t    linux_root_device_thisversion=${GRUB_DEVICE}\n\tfi\n\n\tif [ \"x$is_first_entry\" = xtrue ]; then\n\t    linux_entry \"${OS}\" \"${version}\" \"${xen_version}\" simple \\\n\t\t\"${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}\" \"${GRUB_CMDLINE_XEN} ${GRUB_CMDLINE_XEN_DEFAULT}\"\n\n\t    submenu_indentation=\"$grub_tab$grub_tab\"\n    \n\t    if [ -z \"$boot_device_id\" ]; then\n\t\tboot_device_id=\"$(grub_get_device_id \"${GRUB_DEVICE}\")\"\n\t    fi\n            # TRANSLATORS: %s is replaced with an OS name\n\t    echo \"submenu '$(gettext_printf \"Advanced options with AEM boot for %s (with Xen hypervisor)\" \"${OS}\" | grub_quote)' \\$menuentry_id_option 'gnulinux-advanced-$boot_device_id' {\"\n\techo \"\tsubmenu '$(gettext_printf \"Xen hypervisor, version %s\" \"${xen_version}\" | grub_quote)' \\$menuentry_id_option 'xen-hypervisor-$xen_version-$boot_device_id' {\"\n\tfi\n\tis_first_entry=false\n\n\tlinux_entry \"${OS}\" \"${version}\" \"${xen_version}\" advanced \\\n\t    \"${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}\" \"${GRUB_CMDLINE_XEN} ${GRUB_CMDLINE_XEN_DEFAULT}\"\n\tif [ \"x${GRUB_DISABLE_RECOVERY}\" != \"xtrue\" ]; then\n\t    linux_entry \"${OS}\" \"${version}\" \"${xen_version}\" recovery \\\n\t\t\"single ${GRUB_CMDLINE_LINUX}\" \"${GRUB_CMDLINE_XEN}\"\n\tfi\n\n\tlist=`echo $list | tr ' ' '\\n' | grep -vx $linux | tr '\\n' ' '`\n    done\n    if [ x\"$is_first_entry\" != xtrue ]; then\n\techo '\t}'\n    fi\n    xen_list=`echo $xen_list | tr ' ' '\\n' | grep -vx $current_xen | tr '\\n' ' '`\ndone\n\n# If at least one kernel was found, then we need to\n# add a closing '}' for the submenu command.\nif [ x\"$is_first_entry\" != xtrue ]; then\n  echo '}'\nfi\n\necho \"$title_correction_code\"\n\necho fi\n"
  },
  {
    "path": "sbin/anti-evil-maid-install",
    "content": "#!/bin/bash\nset -euo pipefail\nshopt -s expand_aliases\n. anti-evil-maid-lib\nLABEL_SUFFIX_CHARS=0-9a-zA-Z=.-\nBOOT_DIR=/boot\nGRUB_DIR=$BOOT_DIR/grub2\nGRUB_CFG=$GRUB_DIR/grub.cfg\n\nvalidatetpm || exit 1\n\nusage() {\n    cat <<END\n\nUsage:\n  anti-evil-maid-install [-s <suffix>] [-F] [-m] <device>\n\n  Installs Anti Evil Maid to your system's boot partition, or to a different\n  storage device (e.g. an SD card or a USB stick).\n\n\nArguments:\n  -s: <device> gets labeled \"$LABEL_PREFIX<suffix>\"\n\n      <suffix> can be composed of 0-13 characters from the alphabet\n        $LABEL_SUFFIX_CHARS\n      It defaults to <device>'s current suffix, if any, or the empty string\n      otherwise. Each of your AEM installations must have a unique suffix.\n\n      This suffix has no particular meaning, except that you can let it end\n      in .rm=1 or .rm=0 to hint that <device> is removable or fixed,\n      respectively, no matter what the Linux kernel detects.\n\n  -F: passed on to mkfs.ext4 (don't ask for confirmation, etc.)\n\n  -m: set up a multi-factor auth AEM media\n      Using time-based one time password and a LUKS key file, provides\n      resistance to shoulder surfing and video surveillance based passphrase\n      snooping.\n\n\nExamples:\n  Install on the system's boot partition (assuming that it is /dev/sda1), and\n  label its current filesystem \"$LABEL_PREFIX\":\n\n    anti-evil-maid-install /dev/sda1\n\n  Install on an SD card's first partition, replacing its data with a new ext4\n  filesystem labeled \"$LABEL_PREFIX.sd\", and make it bootable:\n\n    anti-evil-maid-install -s .sd /dev/mmcblk0p1\n\n  Install MFA-enabled AEM on USB stick's first partition, overwriting it with\n  a new ext4 filesystem and marking it bootable:\n\n    anti-evil-maid-install -m /dev/sdb1\n\nEND\n\n    exit 1\n}\n\n\n# check invocation\n\nalias mfa=false\nLABEL_SUFFIX=\nF=()\nwhile getopts s:Fhm opt; do\n    case \"$opt\" in\n        s) LABEL_SUFFIX=$OPTARG ;;\n        F) F=( -F ) ;;\n        m) alias mfa=true ;;\n        *) usage ;;\n    esac\ndone\n\n# shellcheck disable=SC2102\ncase \"$LABEL_SUFFIX\" in *[!$LABEL_SUFFIX_CHARS]*|??????????????*) usage; esac\nLABEL=$LABEL_PREFIX$LABEL_SUFFIX\n\nshift $((OPTIND - 1))\ncase $# in\n    1) PART_DEV=$1 ;;\n    *) usage ;;\nesac\n\nif [ \"$(id -ur)\" != 0 ]; then\n    log \"This command must be run as root!\"\n    exit 1\nfi\n\nif [ -z \"$(getluksuuids)\" ]; then\n    log \"Anti Evil Maid requires encrypted disk!\"\n    exit 1\nfi\n\ntpmstartservices\n\n# examine device\n\nBOOT_MAJMIN=$(mountpoint -d \"$BOOT_DIR\") || BOOT_MAJMIN=\nPART_DEV_MAJMIN=$(lsblk -dnr -o MAJ:MIN \"$PART_DEV\")\n\nif external \"$PART_DEV\" && [ \"$BOOT_MAJMIN\" != \"$PART_DEV_MAJMIN\" ]; then\n    alias replace=true\nelse\n    alias replace=false\nfi\n\nWHOLE_DEV=$(lsblk -dnp -o PKNAME \"$PART_DEV\")\nif [ ! -b \"$WHOLE_DEV\" ] || [ \"$WHOLE_DEV\" == \"$PART_DEV\" ]; then\n    log \"Couldn't find parent device: $WHOLE_DEV\"\n    exit 1\nfi\n\nPART_DEV_REAL=$(readlink -f \"$PART_DEV\")\nPART_NUM=${PART_DEV_REAL##*[!0-9]}\nif ! [ \"$PART_NUM\" -gt 0 ]; then\n    log \"Couldn't extract partition number: $PART_NUM\"\n    exit 1\nfi\n\n\n# MFA-specific checks\n\nif mfa && ! external \"$PART_DEV\"; then\n    log \"WARNING: Installing MFA AEM on the same disk\"\n    log \"as Qubes OS will NOT provide any resistance\"\n    log \"against keyboard observation during boot!\"\n    log \"Additionally, compromise recovery using\"\n    log \"freshness token revocation will be a lot\"\n    log \"less feasible.\"\n    waitforenter\nelif mfa && ! removable \"$PART_DEV\" \"$LABEL\" ; then\n    log \"WARNING: Installing MFA AEM on an internal\"\n    log \"disk will NOT provide any resistance\"\n    log \"against keyboard observation during boot!\"\n    log \"Additionally, compromise recovery using\"\n    log \"freshness token revocation will be a lot\"\n    log \"less feasible.\"\n    log \"You can safely ignore this warning if the\"\n    log \"device in question is, in fact, removable.\"\n    waitforenter\nfi\n\n\n# This check (instead of a more obvious 'mountpoint $BOOT_DIR') should work\n# even in unusual setups without any internal boot partition at all:\n\nif [ ! -e \"$GRUB_CFG\" ]; then\n    log \"Couldn't find boot files at $BOOT_DIR\"\n    exit 1\nfi\n\n\n# keep old label unless overridden explicitly\n\nOLD_LABEL=$(lsblk -dnr -o LABEL \"$PART_DEV\") ||\nOLD_LABEL=\n\ncase \"$OLD_LABEL\" in \"$LABEL_PREFIX\"*)\n    if [ -z \"${LABEL_SUFFIX+set}\" ]; then\n        LABEL=$OLD_LABEL\n    fi\nesac\n\n\n# create and/or label fs\n\nif replace; then\n    log \"Creating new ext4 filesystem labeled $LABEL\"\n    mkfs.ext4 \"${F[@]}\" -L \"$LABEL\" \"$PART_DEV\"\nelse\n    log \"Labeling filesystem $LABEL\"\n    e2label \"$PART_DEV\" \"$LABEL\"\nfi\n\n\n# move secrets if label changed\n\nif [   -n \"$OLD_LABEL\" ] &&\n   [   -e \"$AEM_DIR/$OLD_LABEL\" ] &&\n   [ ! -e \"$AEM_DIR/$LABEL\" ]; then\n    mv -v \"$AEM_DIR/$OLD_LABEL\" \"$AEM_DIR/$LABEL\"\nfi\n\n\n# add the AEM media being created to the freshness database\n\nif suffixtoslot \"$LABEL_SUFFIX\" >/dev/null; then\n    log \"WARNING: (possibly another) AEM media with the same\"\n    log \"label suffix is already enrolled in the freshness token\"\n    log \"database! Overwriting will result in the old AEM media\"\n    log \"failing to perform a successful AEM boot. If you're\"\n    log \"simply reinstalling on the same device or intentionally\"\n    log \"replacing an old AEM media that was lost/destroyed/etc.,\"\n    log \"it is safe to continue.\"\n    read -r -p \"Proceed? [y/N] \" response\n    case \"$response\" in\n        y|Y) echo \"continuing...\" ;;\n        *) exit ;;\n    esac\nelse\n    assignslottosuffix \"$LABEL_SUFFIX\"\n    slot=$(suffixtoslot \"$LABEL_SUFFIX\")\n    log \"Assigned slot $slot to this AEM media\"\nfi\n\n\n# MFA: generate a TOTP seed\n\nif mfa && [ ! -e \"$AEM_DIR/$LABEL/secret.otp\" ]; then\n    log \"Generating new 160-bit TOTP seed\"\n    mkdir -p \"$AEM_DIR/$LABEL\"\n    otp_secret=$(head -c 20 /dev/random | base32 -w 0 | tr -d =)\n    echo \"$otp_secret\" > \"$AEM_DIR/$LABEL/secret.otp\"\n\n    # create an ANSI text QR code and show it in the terminal\n    otp_uri=\"otpauth://totp/${LABEL}?secret=${otp_secret}\"\n    echo -n \"$otp_uri\" | qrencode -t ansiutf8\n    log \"Please scan the above QR code with your OTP device.\"\n\n    # display the text form of secret to user, too\n    # shellcheck disable=SC2001\n    human_readable_secret=\"$(echo \"$otp_secret\" | sed 's/\\(....\\)/\\1\\ /g')\"\n    log \"Alternatively, you may manually enter the following\"\n    log \"secret into your OTP device:\"\n    log \"    $human_readable_secret\"\n\n    if timedatectl status | grep -q 'RTC in local TZ: yes'; then\n        log \"\"\n        log \"WARNING: Your computer's RTC (real-time clock) is set\"\n        log \"to store time in local timezone. This will cause wrong\"\n        log \"TOTP codes to be generated during AEM boot. Please fix\"\n        log \"this by running (as root):\"\n        log \"    timedatectl set-local-rtc 0\"\n        waitforenter\n    fi\n\n    # check whether secret was provisioned correctly\n    log \"\"\n    log \"After you have set up your OTP device, please enter\"\n    log \"the code displayed on your device and press <ENTER>\"\n    log \"to continue.\"\n    log \"\"\n\n    totp_tries=3\n    for try in $(seq $totp_tries); do\n        read -r -p \"Code: \"\n        if ! oathtool --totp -b \"$otp_secret\" \"$REPLY\" >/dev/null; then\n            log \"Entered TOTP code is invalid!\"\n            if [ \"$try\" -lt $totp_tries ]; then\n                log \"Please check clock synchronization.\"\n                log \"If you made mistake while manually entering the secret,\"\n                log \"remove the added token, repeat the process & try again.\"\n                log \"\"\n            else\n                log \"Aborting AEM setup...\"\n                exit 1\n            fi\n        else\n            break\n        fi\n    done\n\n    log \"TOTP code matches, continuing AEM setup.\"\nfi\n\n\n# MFA: generate and enroll a LUKS key file if not already present\n\nif mfa && [ ! -e \"$AEM_DIR/$LABEL/secret.key\" ]; then\n    log \"Generating new LUKS key file\"\n    rawkey=$(mktemp)\n    head -c 64 /dev/random > \"$rawkey\"\n\n    log \"Encrypting key file\"\n    mkdir -p \"$AEM_DIR/$LABEL\"\n    scrypt enc \"$rawkey\" \"$AEM_DIR/$LABEL/secret.key\"\n\n    for uuid in $(getluksuuids); do\n        dev=/dev/disk/by-uuid/$uuid\n        devname=$(readlink -f \"$dev\")\n\n        log \"Adding key file to new key slot for $devname (UUID $uuid)\"\n\n        cryptsetup luksAddKey \"$dev\" \"$rawkey\"\n    done\n\n    log \"Shredding the unencrypted key file\"\n    shred -zu \"$rawkey\"\nfi\n\n\n# mount\n\nif CUR_MNT=$(devtomnt \"$PART_DEV\") && [ -n \"$CUR_MNT\" ]; then\n    PART_MNT=$CUR_MNT\nelse\n    CUR_MNT=\n    PART_MNT=/mnt/anti-evil-maid/$LABEL\n\n    log \"Mounting at $PART_MNT\"\n    mkdir -p \"$PART_MNT\"\n    mount \"$PART_DEV\" \"$PART_MNT\"\nfi\n\n\n# sync\n\nmkdir -p \"$PART_MNT/aem\"\nsynctpms \"$LABEL\" \"$PART_MNT\"\nmkdir -p \"$AEM_DIR/$LABEL\"\n\n\n# make device bootable\n\nif replace; then\n    log \"Setting bootable flag\"\n    parted -s \"$WHOLE_DEV\" set \"$PART_NUM\" boot on\n\n    log \"Copying boot files\"\n    find \"$BOOT_DIR\" -maxdepth 1 -type f ! -name 'initramfs-*.img' \\\n         -exec cp {} \"$PART_MNT\" \\;\n\n    # TODO: If dracut is configured for no-hostonly mode (so we don't have to\n    # worry about picking up loaded kernel modules), just copy each initramfs\n    # instead of regenerating it\n    for img in \"$BOOT_DIR\"/initramfs-*.img; do\n        ver=${img%.img}\n        ver=${ver##*initramfs-}\n        log \"Generating initramfs for kernel $ver\"\n        dracut --force \"$PART_MNT/${img##*/}\" \"$ver\"\n    done\n\n    log \"Copying GRUB themes\"\n    dst=$PART_MNT/${GRUB_DIR#\"$BOOT_DIR\"/}\n    mkdir \"$dst\"\n    cp -r \"$GRUB_DIR/themes\" \"$dst\"\n\n    log \"Installing GRUB\"\n    grub2-install --boot-directory=\"$PART_MNT\" \"$WHOLE_DEV\"\n\n    log \"Bind mounting $PART_MNT at $BOOT_DIR\"\n    mount --bind \"$PART_MNT\" \"$BOOT_DIR\"\nfi\n\nlog \"Generating GRUB configuration\"\ngrub2-mkconfig -o \"$GRUB_CFG\"\n\nif replace; then\n    log \"Unmounting bind mounted $BOOT_DIR\"\n    umount \"$BOOT_DIR\"\nfi\n\n\nif [ -z \"$CUR_MNT\" ]; then\n    log \"Unmounting $PART_MNT\"\n    umount \"$PART_MNT\"\nfi\n"
  },
  {
    "path": "sbin/anti-evil-maid-lib",
    "content": "LABEL_PREFIX=aem\nSYSFS_TPM_DIR=/sys/class/tpm/tpm0\nAEM_DIR=/var/lib/anti-evil-maid\nTPM_DIR=/var/lib/tpm\nTPMS_DIR=${TPM_DIR}s\nCACHE_DIR=/run/anti-evil-maid\nSRK_PASSWORD_CACHE=$CACHE_DIR/srk-password\n# shellcheck disable=SC2034\nSUFFIX_CACHE=$CACHE_DIR/suffix\nTPM_OWNER_PASSWORD_FILE=$AEM_DIR/tpm-owner-pw\nTPM_FRESHNESS_PASSWORD_FILE=$AEM_DIR/tpm-freshness-pw\nTPM_FRESHNESS_INDEX=\"0x454d\"\nTPM_FRESHNESS_SLOTS=8\n\n\n# work with or without plymouth\n\nif command plymouth --ping 2>/dev/null; then\n    alias plymouth_active=true\n    alias message=plymouth_message\nelse\n    alias plymouth=:\n    alias plymouth_active=false\n    alias message=log\nfi\n\n\ngetparams() {\n    _CMDLINE=${_CMDLINE-$(cat /proc/cmdline)}\n\n    for _param in $_CMDLINE; do\n        for _key; do\n            case \"$_param\" in \"$_key\"=*)\n                printf '%s\\n' \"${_param#*=}\"\n                break\n            esac\n        done\n    done\n}\n\ngetluksuuids() {\n    getparams rd.luks.uuid rd_LUKS_UUID | sed s/^luks-//\n}\n\nlog() {\n    echo \"${0##*/}: $1\" >&2\n}\n\nhex() {\n    xxd -ps | tr -dc 0-9a-f\n}\n\nunhex() {\n    tr -dc 0-9a-f | xxd -ps -r\n}\n\nwaitfor() {\n    case $# in\n        2) _file=$2; _what=connected ;;\n        3) _file=$3; _what=removed ;;\n        *) return 1 ;;\n    esac\n\n    if [ \"$@\" ]; then\n        return\n    fi\n\n    message \"Waiting for $_file to be $_what...\"\n    plymouth pause-progress\n    until [ \"$@\" ]; do\n        sleep 0.1\n    done\n    plymouth unpause-progress\n    message \"$_file $_what\"\n}\n\nwaitforenter() {\n    msg='Press <ENTER> to continue...'\n    if plymouth_active; then\n        message \"$msg\"\n        plymouth watch-keystroke --keys=$'\\n'\n    else\n        systemd-ask-password --timeout=0 --echo=no \"$msg\" >/dev/null\n    fi\n}\n\nsuffixtoslotfile() {\n    echo \"$AEM_DIR/$LABEL_PREFIX$1/tpm-freshness-slot\"\n}\n\nsuffixtoslot() {\n    # returns the slot number assigned to the AEM media given its label suffix\n    # as the first argument\n    _slotfile=$(suffixtoslotfile \"$1\")\n    cat \"$_slotfile\" 2>/dev/null\n}\n\nassignslottosuffix() {\n    # assigns an unused freshness slot number (if available) to an AEM\n    # media identified by its label suffix (passed as the first argument)\n    _slotfile=$(suffixtoslotfile \"$1\")\n    rm -f \"$_slotfile\"\n\n    _slotfilesglob=$(suffixtoslotfile '*')\n    _lastslot=$((TPM_FRESHNESS_SLOTS - 1))\n    _freeslot=$(\n        {\n            cat \"$_slotfilesglob\" 2>/dev/null || true\n            seq 0 $_lastslot\n        } | sort -n | uniq -u | head -n 1\n    )\n\n    if [ -z \"$_freeslot\" ]; then\n        message \"No more freshness token slots available!\"\n        return 1\n    fi\n\n    mkdir -p \"${_slotfile%/*}\"\n    echo \"$_freeslot\" >> \"$_slotfile\"\n}\n\nsynctpms() {\n    _label=${1:?}\n    _mnt=${2:?}\n\n    message \"Syncing to $_mnt\"\n\n    _mnt_tpms_dir=$_mnt/aem/${TPMS_DIR##*/}\n    rm -rf \"$_mnt_tpms_dir\"\n\n    _ids=$(ls \"$TPMS_DIR\")\n    for _id in $_ids; do\n        mkdir -p \"$_mnt_tpms_dir/$_id\"\n        # this file is used only with TPM1\n        if [ -f \"$TPMS_DIR/$_id/system.data\" ]; then\n            cp \"$TPMS_DIR/$_id/system.data\" \"$_mnt_tpms_dir/$_id\"\n        fi\n\n        if [ -d \"$TPMS_DIR/$_id/$_label\" ]; then\n            cp -r  \"$TPMS_DIR/$_id/$_label\" \"$_mnt_tpms_dir/$_id\"\n        fi\n    done\n}\n\ndevtomnt() {\n    lsblk -dnr -o MOUNTPOINT \"$1\" 2>/dev/null |\n    sed 's/%/\\\\x25/g' |\n    xargs -0 printf\n}\n\ntopdev() {\n    lsblk -snrp -o KNAME \"$1\" | tail -n 1\n}\n\nexternal() {\n    _aem_whole=$(topdev \"$1\")\n    for _luks_uuid in $(getluksuuids); do\n        _luks_whole=$(topdev \"/dev/disk/by-uuid/$_luks_uuid\")\n        if [ \"$_aem_whole\" = \"$_luks_whole\" ]; then\n            return 1\n        fi\n    done\n    return 0\n}\n\nremovable() {\n    _rm=\"$(lsblk -dnr -o RM \"$1\") ${2-$(lsblk -dnr -o LABEL \"$1\")}\"\n    case \"$_rm\" in\n        *.rm=[01]) _rm=${_rm##*=} ;;\n                *) _rm=${_rm%% *} ;;\n    esac\n\n    [ \"$_rm\" = 1 ]\n}\n\nvalidatetpm() {\n    # makes sure TPM is there and can be used, determines TPM version\n    if [ ! -d \"$SYSFS_TPM_DIR\" ]; then\n        message \"$SYSFS_TPM_DIR isn't present\"\n        return 1\n    fi\n\n    _tpm_version=$(cat \"$SYSFS_TPM_DIR/tpm_version_major\")\n    if [ -z \"$_tpm_version\" ]; then\n        message \"Failed to determine the version of the TPM\"\n        return 1\n    fi\n\n    if [ \"$_tpm_version\" -eq 1 ]; then\n        # shellcheck source=../sbin/anti-evil-maid-lib-tpm1\n        source /sbin/anti-evil-maid-lib-tpm1\n        return 0\n    fi\n\n    if [ \"$_tpm_version\" -eq 2 ]; then\n        # shellcheck source=../sbin/anti-evil-maid-lib-tpm2\n        source /sbin/anti-evil-maid-lib-tpm2\n        return 0\n    fi\n\n    message \"Unexpected TPM version: $_tpm_version\"\n    return 1\n}\n"
  },
  {
    "path": "sbin/anti-evil-maid-lib-tpm1",
    "content": "tpmid() {\n    tpm_id\n}\n\ntpmzsrk() {\n    tpm_z_srk\n}\n\nchecktpmnvram() {\n    # checks whether the TPM NVRAM area is defined\n    # NOTE: tpm_nvinfo does not return non-zero if requested index\n    # is not a defined NVRAM area so we need to parse\n    if ! tpm_nvinfo -i \"$TPM_FRESHNESS_INDEX\" | grep -q 'AUTHWRITE'; then\n        return 1\n    fi\n}\n\ncreatetpmnvram() {\n    # create the world-readable/AUTHWRITE TPM NVRAM area to hold up to\n    # TPM_FRESHNESS_SLOTS anti-replay freshness token hashes;\n    # takes TPM owner password as an agument\n    if [ ! -e \"$TPM_FRESHNESS_PASSWORD_FILE\" ]; then\n        message \"Generating TPM NVRAM area AUTHWRITE password\"\n        head -c 16 /dev/random | hex > \"$TPM_FRESHNESS_PASSWORD_FILE\"\n    fi\n\n    _pw=$(cat \"$TPM_FRESHNESS_PASSWORD_FILE\")\n\n    if ! tpm_nvdefine -i \"$TPM_FRESHNESS_INDEX\" \\\n            -s $((TPM_FRESHNESS_SLOTS * 20)) \\\n            -p AUTHWRITE --pwda=\"$_pw\" --pwdo=\"$1\"; then\n        return 1\n    fi\n}\n\nhashfile() {\n    # computes hash of a file passed as the only argument\n    _path=$1\n    sha1sum \"$_path\" | cut -d ' ' -f 1\n}\n\ncheckfreshness() {\n    # check whether hash of an usealed freshness token (file path\n    # given as an argument) is contained in TPM NVRAM area\n    _hash=$(hashfile \"$1\")\n    _lastslot=$((TPM_FRESHNESS_SLOTS - 1))\n    for _i in $(seq 0 $_lastslot); do\n        _slot=$(tpm_nvread_stdout -i \"$TPM_FRESHNESS_INDEX\" \\\n            -n \"$((_i * 20))\" -s 20 | hex)\n        if [ \"$_hash\" == \"$_slot\" ]; then\n            return 0\n        fi\n    done\n    message \"Freshness token does not match any slot in TPM NVRAM!\"\n    return 1\n}\n\nupdatefreshness() {\n    # takes a path to the new freshness token as an argument and\n    # stores its sha1 hash in the appropriate freshness token slot\n    # of the TPM NVRAM area; second argument is the AEM boot device\n    # label suffix\n    if [ ! -e \"$TPM_FRESHNESS_PASSWORD_FILE\" ]; then\n        message \"TPM NVRAM area AUTHWRITE password file does not exist!\"\n        return 1\n    fi\n\n    if ! _slot=$(suffixtoslot \"$2\"); then\n        message \"Suffix '$2' not in DB, attempting to create...\"\n        if ! _slot=$(assignslottosuffix \"$2\"); then\n            message \"Failed to add suffix '$2' into DB!\"\n            return 1\n        fi\n    fi\n\n    _pw=$(cat \"$TPM_FRESHNESS_PASSWORD_FILE\")\n    hashfile \"$1\" | unhex \\\n    | tpm_nvwrite_stdin -i \"$TPM_FRESHNESS_INDEX\" \\\n      -n \"$((_slot * 20))\" -s 20 --password=\"$_pw\"\n}\n\nrevokefreshness() {\n    # invalidates the freshness token of a specified AEM media (by its\n    # label suffix\n    _suff=$1\n    if _slot=$(suffixtoslot \"$_suff\"); then\n        message \"Revoking freshness token for AEM media w/ suffix '$_suff'...\"\n        _pw=$(cat \"$TPM_FRESHNESS_PASSWORD_FILE\")\n        if tpm_nvwrite -i \"$TPM_FRESHNESS_INDEX\" \\\n                -n \"$((_slot * 20))\" -s 20 \\\n                --password=\"$_pw\" -m \"0xff\"; then\n            message \"Done.\"\n        else\n            message \"Failed!\"\n        fi\n    else\n        message \"AEM device with label suffix '$_suff' not found in DB!\"\n    fi\n}\nresetfreshness() {\n    # invalidates ALL freshness tokens\n    message \"Invalidating **ALL** freshness tokens...\"\n    _pw=$(cat \"$TPM_FRESHNESS_PASSWORD_FILE\")\n    if tpm_nvwrite -i \"$TPM_FRESHNESS_INDEX\" \\\n            -s \"$((TPM_FRESHNESS_SLOTS * 20))\" \\\n            --password=\"$_pw\" -m \"0xff\"; then\n        message \"Done.\"\n    else\n        message \"Failed!\"\n    fi\n}\n\ndestroytpmnvram() {\n    # releases the TPM NVRAM area; TPM owner pw as first argument\n    tpm_nvrelease -i \"$TPM_FRESHNESS_INDEX\" --pwdo=\"$1\"\n}\n\nlistbadpcrs() {\n    # prints those standard PCRs configured to be used via $SEAL which haven't\n    # been extended, output is empty there are no such PCRs\n    _pcrs=$(printf %s \"$SEAL\" | grep -Eo '\\b1[3789]\\b') || true\n    grep -E \"^PCR-(${_pcrs//$'\\n'/|}):( 00| FF){20}\" \"$SYSFS_TPM_DIR\"/pcrs\n}\n\ntpmowned() {\n    # checks whether TPM is already owned, signals results with exit code\n    [ \"$(cat \"$SYSFS_TPM_DIR\"/owned)\" -ne 0 ]\n}\n\nprovisiontpmid() {\n    # stores TPM ID into an NVRAM entry \n    _tpm_id_index=$(tpm_id -i)\n    _opw=$(cat \"$TPM_OWNER_PASSWORD_FILE\")\n    # create a write-once NVRAM area\n    tpm_nvdefine -i \"$_tpm_id_index\" -s 20 -p \"WRITEDEFINE|WRITEALL\" \\\n                 --pwdo=\"$_opw\"\n    # generate a random ID and write it into NVRAM\n    head -c 20 /dev/random | tpm_nvwrite_stdin -i \"$_tpm_id_index\" -s 20\n    # lock the area to prevent non-owners from changing ID\n    tpm_nvwrite -i \"$_tpm_id_index\" -s 0\n}\n\npostprovisioning() {\n    # takes care of updating /var/lib/tpms after a successful provisioning by\n    # provisiontpmid\n    _tpmid=$(tpm_id)\n    mkdir -p \"/var/lib/tpms/$_tpmid\"\n    systemctl stop tcsd\n    mv \"$TPMS_DIR\"/unknown/* \"$TPMS_DIR/$_tpmid/\"\n    rm -rf \"$TPMS_DIR/unknown\"\n    systemctl start tcsd\n}\n\nchecksrkpass() {\n    # checks whether contents of $SRK_PASSWORD_CACHE file is a valid SRK\n    # password, signals result with exit code\n    tpm_sealdata -i /dev/null -o /dev/null < \"$SRK_PASSWORD_CACHE\"\n}\n\ntpmpcrextend() {\n    # extends a PCR with a hash value of a suitable type\n    _pcr=$1\n    _hash=$2\n    tpm_pcr_extend \"$_pcr\" \"$_hash\"\n}\n\ntpmsealprepare() {\n    # does necessary preparations before the use of tpmsealdata, accepts path\n    # to media-specific storage of sealed data\n    true # nothing to do for TPM1\n}\n\ntpmsealdata() {\n    # seals source specified by second argument into destination specified by\n    # the third one, non-empty first argument signifies empty SRK password, the\n    # forth argument specifies path to AEM media-specific storage\n    _nosrkpass=()\n    if [ -n \"$1\" ]; then\n        _nosrkpass=( -z )\n    fi\n    _input=$2\n    _output=$3\n    # shellcheck disable=SC2086\n    if [ ! -t 0 ]; then cat \"$SRK_PASSWORD_CACHE\"; fi |\n      tpm_sealdata \"${_nosrkpass[@]}\" $SEAL -i \"$_input\" -o \"$_output\"\n}\n\ntpmunsealdata() {\n    # unseals source specified by second argument into destination specified by\n    # the third one, non-empty first argument signifies empty SRK password, the\n    # forth argument specifies path to AEM media-specific storage\n    _nosrkpass=()\n    if [ -n \"$1\" ]; then\n        _nosrkpass=( -z )\n    fi\n    _infile=$2\n    _outfile=$3\n    tpm_unsealdata \"${_nosrkpass[@]}\" -i \"$_infile\" -o \"$_outfile\" \\\n                 < \"$SRK_PASSWORD_CACHE\"\n}\n\ntpmtakeownership() {\n    # takes ownership of the TPM, accepts owner and SRK passwords in this order\n    _opw=$1\n    _srkpw=$2\n\n    _lines=( \"$_opw\" \"$_opw\" )\n    _nosrkpass=()\n    if [ -n \"$_srkpw\" ]; then\n        _lines+=( \"$_srkpw\" \"$_srkpw\" )\n    else\n        _nosrkpass=( -z )\n    fi\n\n    printf '%s\\n' \"${_lines[@]}\" |\n      notty env LC_ALL=C tpm_takeownership \"${_nosrkpass[@]}\" \\\n          2> >(grep -vF \"Confirm password:\" >&2)\n}\n\ntpmresetdalock() {\n    notty tpm_resetdalock <\"$TPM_OWNER_PASSWORD_FILE\"\n}\n\ntpmstartservices() {\n    systemctl start tcsd\n}\n\ntpmstartinitrdservices() {\n    trousers_changer_identify\n    # it forks\n    tcsd\n}\n\ntpmrestartservices() {\n    systemctl restart tcsd\n}\n"
  },
  {
    "path": "sbin/anti-evil-maid-lib-tpm2",
    "content": "# Value recommended by TCG TPM v2.0 Provisioning Guidance\n# https://trustedcomputinggroup.org/wp-content/uploads/TCG-TPM-v2.0-Provisioning-Guidance-Published-v1r1.pdf\n# in Table 2\nTPM2_SRK_HANDLE=0x81000001\n\n# this is necessary for anti-evil-maid-seal to not try to use tarbmd TCTI which\n# has large timeouts for trying to connect with tpm2-abrmd which isn't running\nexport TPM2TOOLS_TCTI=\"device:/dev/tpm0\"\n\n# make sure we're not leaving any temporary state in the TPM (some very\n# unobvious commands create sessions/objects), this way if tpm2-abrmd will be\n# used later, its idea about the initial contents of the TPM being empty will\n# be correct (otherwise you can get \"out of memory\", but tpm2_getcap won't show\n# anything and tpm2_flushcontext won't clean anything unless $TPM2TOOLS_TCTI\n# is set as above)\ntrap 'tpm2_flushcontext -tls' EXIT\n\ntpmid() {\n    tpm2_id\n}\n\ntpmzsrk() {\n    tpm2_z_srk\n}\n\nchecktpmnvram() {\n    # checks whether the TPM NVRAM area is defined\n    # NOTE: tpm2_nvreadpublic returns all defined NV indices so we need to parse\n    tpm2_nvreadpublic | grep \"$TPM_FRESHNESS_INDEX\" -A 7 | grep -q 'authwrite'\n}\n\ncreatetpmnvram() {\n    # create the world-readable/AUTHWRITE TPM NVRAM area to hold up to\n    # TPM_FRESHNESS_SLOTS anti-replay freshness token hashes;\n    # takes TPM owner password as an agument\n    if [ ! -e \"$TPM_FRESHNESS_PASSWORD_FILE\" ]; then\n        message \"Generating TPM NVRAM area AUTHWRITE password\"\n        head -c 16 /dev/random | hex > \"$TPM_FRESHNESS_PASSWORD_FILE\"\n    fi\n\n    _opw=$1\n    _pw=$(cat \"$TPM_FRESHNESS_PASSWORD_FILE\")\n    _session=\"$(mktemp)\"\n    _policy=\"$(mktemp)\"\n    tpm2_startauthsession -S \"$_session\"\n    tpm2_policycommandcode -Q -S \"$_session\" -L \"$_policy\" \\\n                           TPM2_CC_NV_Read\n    tpm2_flushcontext \"$_session\"\n    rm \"$_session\"\n    tpm2_nvdefine -Q \"$TPM_FRESHNESS_INDEX\" \\\n                  -L \"$_policy\" \\\n                  -a \"policyread|authread|authwrite\" \\\n                  -s $((TPM_FRESHNESS_SLOTS * 32)) \\\n                  -P \"$_opw\" -p \"$_pw\"\n    rm \"$_policy\"\n}\n\nhashfile() {\n    # computes hash of a file passed as the only argument\n    _path=$1\n    sha256sum \"$_path\" | cut -d ' ' -f 1\n}\n\ncheckfreshness() {\n    # check whether hash of an usealed freshness token (file path\n    # given as an argument) is contained in TPM NVRAM area\n    _hash=$(hashfile \"$1\")\n    _lastslot=$((TPM_FRESHNESS_SLOTS - 1))\n    tpm2_startauthsession -S \"$CACHE_DIR/session\" --policy-session\n    tpm2_policycommandcode -Q -S \"$CACHE_DIR/session\" TPM2_CC_NV_Read\n    for _i in $(seq 0 $_lastslot); do\n        _slot=$(tpm2_nvread \"$TPM_FRESHNESS_INDEX\" \\\n                            -P \"session:$CACHE_DIR/session\" \\\n                            --offset=\"$((_i * 32))\" -s 32 | hex)\n        if [ \"$_hash\" == \"$_slot\" ]; then\n            tpm2_flushcontext \"$CACHE_DIR/session\"\n            return 0\n        fi\n    done\n    tpm2_flushcontext \"$CACHE_DIR/session\"\n    message \"Freshness token does not match any slot in TPM NVRAM!\"\n    return 1\n}\n\nupdatefreshness() {\n    # takes a path to the new freshness token as an argument and\n    # stores its hash in the appropriate freshness token slot\n    # of the TPM NVRAM area; second argument is the AEM boot device\n    # label suffix\n    _file=$1\n    if [ ! -e \"$TPM_FRESHNESS_PASSWORD_FILE\" ]; then\n        message \"TPM NVRAM area AUTHWRITE password file does not exist!\"\n        return 1\n    fi\n\n    if ! _slot=$(suffixtoslot \"$2\"); then\n        message \"Suffix '$2' not in DB, attempting to create...\"\n        if ! _slot=$(assignslottosuffix \"$2\"); then\n            message \"Failed to add suffix '$2' into DB!\"\n            return 1\n        fi\n    fi\n\n    _pw=$(cat \"$TPM_FRESHNESS_PASSWORD_FILE\")\n    hashfile \"$_file\" | unhex |\n      tpm2_nvwrite \"$TPM_FRESHNESS_INDEX\" -i - \\\n                   --offset \"$((_slot * 32))\" -P \"$_pw\"\n}\n\nffbytestream() {\n    _count=$1\n    tr '\\0' '\\377' < /dev/zero | dd bs=\"$_count\" count=1\n}\n\nrevokefreshness() {\n    # invalidates the freshness token of a specified AEM media (by its\n    # label suffix\n    _suff=$1\n    if _slot=$(suffixtoslot \"$_suff\"); then\n        message \"Revoking freshness token for AEM media w/ suffix '$_suff'...\"\n        _pw=$(cat \"$TPM_FRESHNESS_PASSWORD_FILE\")\n        if ffbytestream 32 |\n             tpm2_nvwrite \"$TPM_FRESHNESS_INDEX\" \\\n                          --offset \"$((_slot * 32))\" \\\n                          -P \"$_pw\" -i - ; then\n            message \"Done.\"\n        else\n            message \"Failed!\"\n        fi\n    else\n        message \"AEM device with label suffix '$_suff' not found in DB!\"\n    fi\n}\n\nresetfreshness() {\n    # invalidates ALL freshness tokens\n    message \"Invalidating **ALL** freshness tokens...\"\n    _pw=$(cat \"$TPM_FRESHNESS_PASSWORD_FILE\")\n    if ffbytestream \"$((TPM_FRESHNESS_SLOTS * 32))\" |\n         tpm2_nvwrite \"$TPM_FRESHNESS_INDEX\" \\\n                      -P \"$_pw\" -i - ; then\n        message \"Done.\"\n    else\n        message \"Failed!\"\n    fi\n}\n\ndestroytpmnvram() {\n    # releases the TPM NVRAM area; TPM owner pw as first argument\n    _pw=$1\n    tpm2_nvundefine \"$TPM_FRESHNESS_INDEX\" -C owner -P \"$_pw\"\n}\n\nlistbadpcrs() {\n    # prints those standard PCRs configured to be used via $SEAL which haven't\n    # been extended, output is empty there are no such PCRs\n    _pcrs=$(printf %s \"$SEAL\" | grep -Eo '\\b1[3789]\\b') || true\n    tpm2_pcrread \"sha256:${_pcrs//$'\\n'/,}\" | grep -E \": 0x((00){32}|(FF){32})\"\n}\n\ntpmowned() {\n    # checks whether TPM is already owned, signals results with exit code\n    ! tpm2_changeauth --quiet -c owner 2>/dev/null\n}\n\nprovisiontpmid() {\n    # stores TPM ID into an NVRAM entry\n    _tpm_id_index=$(tpm2_id -i)\n    _opw=$(cat \"$TPM_OWNER_PASSWORD_FILE\")\n    # create a write-once and read-by-anyone NVRAM area\n    _session=\"$(mktemp)\"\n    _policy=\"$(mktemp)\"\n    tpm2_startauthsession -S \"$_session\"\n    tpm2_policycommandcode -Q -S \"$_session\" -L \"$_policy\" TPM2_CC_NV_Read\n    tpm2_flushcontext \"$_session\"\n    rm \"$_session\"\n    tpm2_nvdefine -Q -s 20 -P \"$_opw\" -L \"$_policy\" \"$_tpm_id_index\" \\\n                  -a \"policyread|writedefine|writeall|ownerwrite|ownerread\"\n    rm \"$_policy\"\n    # generate a random ID and write it into NVRAM\n    head -c 20 /dev/random |\n      tpm2_nvwrite -Q -C o -P \"$_opw\" -i - \"$_tpm_id_index\"\n    # lock the area to prevent changing the ID even by the owner\n    tpm2_nvwritelock -C o -P \"$_opw\" \"$_tpm_id_index\"\n}\n\npostprovisioning() {\n    # takes care of updating /var/lib/tpms after a successful provisioning by\n    # provisiontpmid\n    _tpmid=$(tpm2_id)\n    mkdir -p \"/var/lib/tpms/$_tpmid\"\n}\n\nchecksrkpass() {\n    # checks whether contents of $SRK_PASSWORD_CACHE file is a valid SRK\n    # password, signals result with exit code\n\n    # `echo` is needed because empty input doesn't work and `echo` it provides\n    # '\\n'\n    echo | tpm2_create -Q -C \"$TPM2_SRK_HANDLE\" -i - \\\n                       -P \"str:$(cat \"$SRK_PASSWORD_CACHE\")\"\n}\n\ntpmpcrextend() {\n    # extends a PCR with a hash value of a suitable type\n    _pcr=$1\n    _hash=$2\n    tpm2_pcrextend \"$_pcr:sha256=$_hash\"\n}\n\ntpmsealprepare() {\n    # does necessary preparations before the use of tpmsealdata, accepts path\n    # to media-specific storage of sealed data\n    _dir=$1\n\n    # this recreates a sealing key on every run to pick up configuration/PCR\n    # changes if there were any\n\n    _pcrs=$(printf %s \"$SEAL\" | grep -Eo '\\b[0-9]+\\b')\n    _pcrs=sha256:${_pcrs//$'\\n'/,}\n\n    _policy=\"$(mktemp)\"\n    _session=\"$(mktemp)\"\n\n    # make a suitable PCR policy\n    tpm2_startauthsession -S \"$_session\"\n    tpm2_policypcr -Q -S \"$_session\" -l \"$_pcrs\" -L \"$_policy\"\n    tpm2_flushcontext \"$_session\"\n    rm \"$_session\"\n\n    tpm2_flushcontext -t || return 1\n\n    # make a key for sealing\n    head -c 16 /dev/random |\n        tpm2_create -Q -C \"$TPM2_SRK_HANDLE\" \\\n                    -P \"str:$(cat \"$SRK_PASSWORD_CACHE\")\" \\\n                    -L \"$_policy\" -i - -u \"$_dir/key.pub\" -r \"$_dir/key.priv\"\n    echo \"$_pcrs\" > \"$_dir/key.pcrs\"\n\n    rm \"$_policy\"\n}\n\ntpmsealdata() {\n    # seals source specified by second argument into destination specified by\n    # the third one, non-empty first argument signifies empty SRK password, the\n    # forth argument specifies path to AEM media-specific storage\n    _infile=$2\n    _outfile=$3\n    _dir=$4\n\n    _ctx=\"$(mktemp)\"\n    tpm2_load -Q -C \"$TPM2_SRK_HANDLE\" -P \"str:$(cat \"$SRK_PASSWORD_CACHE\")\" \\\n              --private \"$_dir/key.priv\" --public \"$_dir/key.pub\" \\\n              -c \"$_ctx\" || return 1\n\n    tpm2_unseal -Q -c \"$_ctx\" -p \"pcr:$_pcrs\" | hex |\n        openssl enc -aes-256-ctr -pbkdf2 -e \\\n                    -kfile - -in \"$_infile\" -out \"$_outfile\" || return 1\n\n    tpm2_flushcontext -t || return 1\n    rm \"$_ctx\"\n}\n\ntpmunsealdata() {\n    # unseals source specified by second argument into destination specified by\n    # the third one, non-empty first argument signifies empty SRK password, the\n    # forth argument specifies path to AEM media-specific storage\n    #\n    # there is not need to handle the first argument, empty $SRK_PASSWORD_CACHE\n    # file will do\n    _infile=$2\n    _outfile=$3\n    _dir=$4\n\n    _pcrs=$(cat \"$_dir/key.pcrs\")\n    _ctx=\"$(mktemp)\"\n    tpm2_load -Q -C \"$TPM2_SRK_HANDLE\" -P \"str:$(cat \"$SRK_PASSWORD_CACHE\")\" \\\n              --private \"$_dir/key.priv\" --public \"$_dir/key.pub\" \\\n              -c \"$_ctx\" 2>/dev/null || return 1\n\n    tpm2_unseal -Q -c \"$_ctx\" -p \"pcr:$_pcrs\" | hex |\n        openssl enc -aes-256-ctr -pbkdf2 -d \\\n                    -kfile - -in \"$_infile\" -out \"$_outfile\" || return 1\n\n    tpm2_flushcontext -t || return 1\n    rm \"$_ctx\"\n}\n\ntpmtakeownership() {\n    # takes ownership of the TPM, accepts owner and SRK passwords in this order\n    _opw=$1\n    _srkpw=$2\n    tpm2_changeauth --quiet -c owner \"$_opw\"\n    # use the same password for lockout handle\n    tpm2_changeauth --quiet -c lockout \"$_opw\"\n\n    _srkctx=\"$(mktemp)\"\n    tpm2_createprimary -Q --hierarchy=o \\\n                       --key-context=\"$_srkctx\" \\\n                       --key-auth=\"$_srkpw\" \\\n                       -P \"$_opw\"\n    # make SRK key persistent\n    tpm2_evictcontrol -Q -C o -P \"$_opw\" -c \"$_srkctx\" \"$TPM2_SRK_HANDLE\"\n    rm \"$_srkctx\"\n}\n\ntpmresetdalock() {\n    tpm2_dictionarylockout -p \"$(cat \"$TPM_OWNER_PASSWORD_FILE\")\" \\\n                           --clear-lockout\n}\n\ntpmstartservices() {\n    trousers_changer_migrate || true\n    trousers_changer_identify\n}\n\ntpmstartinitrdservices() {\n    trousers_changer_identify\n}\n\ntpmrestartservices() {\n    trousers_changer_migrate 2>/dev/null || true\n    trousers_changer_identify 2>/dev/null\n}\n"
  },
  {
    "path": "sbin/anti-evil-maid-seal",
    "content": "#!/bin/bash\nset -euo pipefail -o errtrace\nshopt -s expand_aliases\n\nalias plymouth_message=\"plymouth message --text\"\nsource anti-evil-maid-lib\ntrap 'rm -rf \"$CACHE_DIR\"' EXIT\n\nvalidatetpm || exit 1\n\n# Listing foo.service in anti-evil-maid-seal.service's Requires= and After=\n# would cause it to always be started (even when not booting in AEM mode or\n# when sealing is unnecessary) due to the way systemd evaluates conditions.\n# Putting the systemctl command in ExecStartPre= is also insufficient: The\n# user might want to run this script manually after changing the secret(s).\n\ntpmstartservices\n\nif [ ! -e \"$SUFFIX_CACHE\" ] && [ $# -ne 1 ]; then\n    message \"AEM media suffix cache file does not exist\"\n    message \"and you didn't specify a suffix as the\"\n    message \"first positional argument to this script.\"\n    exit 1\nfi\n\n# scream loudly if sealing fails for some reason\n# (eg. AEM media read-only)\n_failure() {\n    message \"Failed to seal secrets (error @ line $1)!\"\n    waitforenter\n    exit 1\n}\ntrap '_failure $LINENO' ERR\n\n\n# define sealing and device variables\n\n# shellcheck source=../etc/anti-evil-maid.conf\nsource /etc/anti-evil-maid.conf\ntpmresetdalock || true\nZ=$(tpmzsrk)\nLABEL_SUFFIX=${1-$(cat \"$SUFFIX_CACHE\")}\nLABEL=$LABEL_PREFIX$LABEL_SUFFIX\n\ncase $# in\n    0) DEV=/dev/disk/by-uuid/$(getparams aem.uuid) ;;\n    1) DEV=/dev/disk/by-label/$LABEL ;;\n    *) exit 1 ;;\nesac\n\n\n# ensure that all standard PCRs configured to be used have been extended\n\nbad_pcrs=$(listbadpcrs) || true\nif [ -n \"$bad_pcrs\" ]; then\n    message \"PCR sanity check failed!\"\n    message \"Bad PCRs:\"$'\\n'\"$bad_pcrs\"\n    message \"See /usr/share/doc/anti-evil-maid/README for details.\"\n    exit 1\nfi\n\n\n# regenerate the freshness token and store its hash in TPM\n\nhead -c 20 /dev/random > \"$AEM_DIR/$LABEL/secret.fre\"\nupdatefreshness \"$AEM_DIR/$LABEL/secret.fre\" \"$LABEL_SUFFIX\"\n\n\n# seal and save secret(s) to root partition\n\nmkdir -p \"$TPM_DIR/$LABEL\"\ntpmsealprepare \"$TPM_DIR/$LABEL\"\n\nSEALED=0\nfor ext in txt key otp fre; do\n     input=$AEM_DIR/$LABEL/secret.$ext\n    output=$TPM_DIR/$LABEL/secret.$ext.sealed2\n\n    if [ ! -e \"$input\" ]; then\n        message \"Absent $input\"\n    elif tpmsealdata \"$Z\" \"$input\" \"$output\" \"$TPM_DIR/$LABEL\"; then\n        rm -f \"${output%2}\"\n        SEALED=$((SEALED + 1))\n        message \"Sealed $input using $SEAL\"\n    else\n        message \"Failed $input\"\n    fi\ndone\n\nif [ \"$SEALED\" = 0 ]; then\n    exit 1\nfi\n\n\n# mount device\n\nwaitfor -b \"$DEV\"\n\nif CUR_MNT=$(devtomnt \"$DEV\") && [ -n \"$CUR_MNT\" ]; then\n    MNT=$CUR_MNT\nelse\n    CUR_MNT=\n    MNT=/mnt/anti-evil-maid/$LABEL\n    mkdir -p \"$MNT\"\n    mount \"$DEV\" \"$MNT\"\nfi\n\n\n# copy secret(s) to device\n\nsynctpms \"$LABEL\" \"$MNT\"\n\n\n# unmount device\n\nif [ -z \"$CUR_MNT\" ]; then\n    umount \"$MNT\"\n    if external \"$DEV\" && removable \"$DEV\"; then\n        waitfor ! -b \"$DEV\"\n    fi\nfi\n"
  },
  {
    "path": "sbin/anti-evil-maid-tpm-setup",
    "content": "#!/bin/bash\nset -euo pipefail\nshopt -s expand_aliases\n\nsource anti-evil-maid-lib\n\nvalidatetpm || exit 1\n\n\nif ! { [ $# = 0 ] || { [ $# = 1 ] && [ \"$1\" = \"-z\" ]; }; } then\n    echo \"Usage: ${0##*/} [-z]\"\n    exit 1\nfi\n\nif [ \"$(id -ur)\" != 0 ]; then\n    log \"This command must be run as root!\"\n    exit 1\nfi\n\nif tpmowned; then\n    log \"You must reset/clear your TPM chip first!\"\n    exit 1\nfi\n\n\n# - take ownership of TPM\n\nOWNERPW=$(head -c 16 /dev/random | hex)\nsrkpw=\n\nif [ $# = 0 ]; then  # set an SRK password\n    for try in 1 2 3; do\n        read -r -s -p \"Choose SRK password: \"  srkpw\n        echo\n        read -r -s -p \"Confirm SRK password: \" srkpw2\n        echo\n\n        [ \"$srkpw\" != \"$srkpw2\" ] || break\n        log \"Passwords didn't match\"\n        [ \"$try\" != 3 ] || exit 1\n    done\nfi\n\ntpmrestartservices\n\nlog \"Taking ownership of the TPM...\"\ntpmtakeownership \"$OWNERPW\" \"$srkpw\"\n\necho \"$OWNERPW\" >\"$TPM_OWNER_PASSWORD_FILE\"\n\n\n# - generate NVRAM ID\n\nalias provisioned_id=false\nif [[ $(tpmid 2>/dev/null) == \"unknown\" ]]; then\n    # TPM reset does not clear NVRAM, reusing old ID is fine though\n    log \"Creating TPM ID...\"\n    provisiontpmid\n    alias provisioned_id=true\nfi\n\n\n# - create freshness token area\n\nif checktpmnvram; then\n    # delete old freshness area as the old access password is most likely lost\n    # (in case it isn't, the area will simply get recreated with the same pw)\n    log \"Deleting old freshness token NVRAM area...\"\n    destroytpmnvram \"$OWNERPW\"\nfi\nlog \"Creating freshness token NVRAM area...\"\ncreatetpmnvram \"$OWNERPW\"\n\n\n# - update TPMs directory after provisioning\n\nif provisioned_id; then\n    postprovisioning\nfi\n"
  },
  {
    "path": "systemd/system/anti-evil-maid-check-mount-devs.service",
    "content": "[Unit]\nDescription=Anti Evil Maid system mount dev check\nDefaultDependencies=no\nConditionKernelCommandLine=aem.uuid\nBefore=initrd-root-fs.target sysroot.mount swap.target\nAfter=dracut-initqueue.service\nAfter=cryptsetup.target\n\n[Service]\nType=oneshot\nRemainAfterExit=no\nExecStart=/sbin/anti-evil-maid-check-mount-devs\nStandardOutput=journal+console\nStandardError=journal+console\n"
  },
  {
    "path": "systemd/system/anti-evil-maid-seal.service",
    "content": "[Unit]\nDescription=Anti Evil Maid sealing\nDefaultDependencies=false\nRequires=local-fs.target\nAfter=local-fs.target plymouth-start.service\nBefore=basic.target\nConditionKernelCommandLine=aem.uuid\nConditionPathIsDirectory=/run/anti-evil-maid\n\n[Service]\nExecStart=/usr/sbin/anti-evil-maid-seal\nType=oneshot\nStandardOutput=journal+console\nStandardError=inherit\nTimeoutStartSec=300\n\n[Install]\nWantedBy=basic.target\n"
  },
  {
    "path": "systemd/system/anti-evil-maid-unseal.service",
    "content": "[Unit]\nDescription=Anti Evil Maid unsealing\nDefaultDependencies=no\nWants=cryptsetup-pre.target\nBefore=cryptsetup-pre.target\nAfter=plymouth-start.service\nConditionKernelCommandLine=aem.uuid\n\n[Service]\nType=oneshot\nRemainAfterExit=no\nExecStart=/sbin/anti-evil-maid-unseal\nStandardInput=null\nStandardOutput=tty\nStandardError=journal+console\nTimeoutStartSec=300\n"
  },
  {
    "path": "systemd/system/tcsd.service.d/anti-evil-maid-seal.conf",
    "content": "# start early enough for anti-evil-maid-seal.service\n\n[Unit]\nDefaultDependencies=false\n# for trousers_changer_identify:\nRequires=local-fs.target\nAfter=local-fs.target\n"
  },
  {
    "path": "version",
    "content": "4.2.1\n"
  }
]