Giter VIP home page Giter VIP logo

sed-opal-unlocker's Introduction

sed-opal-unlocker

Micro-utility for unlocking TCG-OPAL encrypted disks, utilizing CONFIG_BLK_SED_OPAL interface introduced in kernel 4.11. Also allows saving password in the running kernel for S3 Sleep support, cause it was a cheap feature to have. Based on Kyle Manna's opalctl nano-utility.

Background

I'm using this tool to unlock:

  • non-boot disk from custom initramfs, loaded from EFI partition sitting on another non-opal-encrypted drive. The machine is headless, cannot boot from NVME and the password is provided on an USB key, so the standard sedutil Pre-Boot Authentication image is not an option
  • boot disk from custom PBA image (Linux EFI stub kernel + embedded initramfs) in my laptop... just cause I could.

Features

  • unlocking / locking TCG-OPAL compatible Self-Encrypting Drives
  • turning off MBR shadowing after unlocking
  • saving drive password into the Linux kernel (unlock support when the system is waking from S3 sleep)
  • reads password from a separate file
  • supports password hashing used by sedutil-cli via separate one-time-use script
  • supports SATA and NVME disks
  • password file can be encrypted with another passphrase (requires libargon2)

What this utility cannot do

  • cannot unlock system drive (unless you create a custom Pre-Boot Authentication image with it; however s3save operation is supported when drive gets unlocked with sedutil's PBA image)
  • disk password cannot be read interactively, nor from cmdline argument (however you can encrypt the password file and unlock passphrase is obtained from stdin)
  • will not work with CONFIG_BLK_SED_OPAL=n kernel (and is Linux only, but this should be obvious now)

Building

Just:

    make

If you need a static binary:

    make STATIC=1

If you wish to not depend on libargon2 at cost of disabling encrypted password file support (may be combined with STATIC=1):

    make ENCRYPTED_PASSWORDS=0

If you are Gentoo Linux user, you will find an ebuild in my overlay.

Usage

    sed-opal-unlocker <operation> <disk_path> <password_file_path>

Where:

  • <operation> is one of: lock, unlock, MBRunshadow, MBRunshadowOLD, s3save or comma-separated space-less combination of them (except lock)
  • <disk_path> is device path, eg. /dev/sda, /dev/nvme0n1, etc.
  • <password_file_path> is path to file containing the disk password; if password file is encrypted, passphrase for decrypting it is read from stdin.

Operation specifies what the tool should do:

  • lock: lock the drive. Useful mainly for testing.
  • unlock: unlock the drive. The main feature of this tool.
  • MBRunshadow: disable MBR shadow image. Use after unlock when the disk has been configured to shadow MBR (see below).
  • MBRunshadowOLD: the same as MBRunshadow, but for older kernels (see below).
  • s3save: store password in the Linux kernel for enabling drive unlock after S3 sleep.

When the disk has been initialized with sedutil-cli without using its -n option, the password which is send to the disk is a hash calculated using PKBDF2 algorithm from plain text password and the disk serial for salting. In order to use such password with sed-opal-unlocker, all you need to do is to store the hashed password in the password file. Fortunately, there's a Python script which will do this for you.

    sedutil-passhasher.py <disk_path> <output_passwordhash_file_path> [encrypt_password]

You need to call this script once, as root, cause it reads serial number from the disk needed to salt the password for hashing. Plaintext disk password is entered on script standard input. Hashed password (with some magic value for file type recognition) is written to the output file specified by second argument. Note that the file will be overwritten when it exists.

When the last optional argument, [encrypt_password] is provided and set to 1, the hashed password file will be encrypted using additional "unlock passphrase", also interactively asked on standard input. The unlock passphrase can be optionally salted with current machine's DMI data (serial number or UUID), which makes it usable only on this machine. (This can be hacked around of course, but attacker needs to know this data cause it's not stored in the encrypted password file). When an encrypted password file is provided to sed-opal-unlocker, it will ask for the unlock passphrase on stdin. Note that password encryption currently cannot be used when disk has been initialized without password hashing (sedutil -n).

Please also note that the encrypted password file does not store any authentication/verification data. Had the attacker obtained an encrypted passwordfile, he/she still cannot bruteforce it, cause only the disk can tell whether the unlock passphrase is correct or not. Even having access to the disk does not make bruteforcing easier, cause (a) argon2, (b) OPAL disks have limit how many times you may enter wrong password, and then will require a power-cycle to start talking to you again.

MBRunshadow vs MBRunshadowOLD

Since kernel 5.2 and stable kernels 5.1.6, 5.0.20, 4.19.47 and 4.14.123, a bug in kernel API used by sed-opal-unlocker to switch off MBR shadowing has been fixed. However, the fix goes against the policy of not breaking userspace applications and now sed-opal-unlocker needs to be fixed as well... Cause it's not possible to easily tell whether the kernel is fixed or not, since version 0.3.1 the sed-opal-unlocker has switched to new (fixed) kernel API. However a new operation, MBRunshadowOLD, has been added to still support older (unfixed) kernels. The side effect of using wrong action (MBRunshadow on an old kernel or MBRunshadowOLD on a new kernel) is disabling the MBREnable flag, which means the whole shadowing is deactivated and PBA image is not presented to the host, so the system cannot boot from such device. In order to recover, use correct sed-opal-unlocker operation once or use sedutil-cli --setMBREnable on. See this kernel commit for more info.

Also, please note that kernel 5.3 is going to have a better API for switching MBRDone flag and I'm already planning to utilize it in the sed-opal-unlocker. Please expect another rework in this area in a near future.

Bonus: disk initialization notes

The most helpful information source for me was Self-Encrypting Drives article on Archlinux wiki. Another source worth looking at is sedutil wiki.

Despite I'm encrypting non-root (secondary) disk, I still prefer to enable MBR shadowing and filling it with zeros. Otherwise when kernel boots and tries to read partition table while the disk is still locked, scary looking IO errors are generated, and disk also saves them in some SMART error counter.

Please note that tinkering with your drive may cause data loss. It's best to work with an empty drive, so you lose nothing when screwing up. Otherwise, HAVE A BACKUP.

Do not execute this for your root drive. It won't boot without a proper PBA image.

In all the following examples, replace /dev/disk with proper path (like /dev/sda or /dev/nvme0n1), and "password1234" with your real password. Non-indented line represents command to be executed, following indented lines are its example output.

  1. Initial setup
sedutil-cli --initialsetup password1234 /dev/disk
    takeOwnership complete
    Locking SP Activate Complete
    LockingRange0 disabled
    LockingRange0 set to RW
    MBRDone set on
    MBRDone set on
    MBREnable set on
    Initial setup of TPer complete on /dev/disk
  1. Clear MBR shadow image

(sedutil-cli requires a file to load, therefore first we need to create image filled with zeros. Copying takes a while and it's better not to interrupt it - disk may hang, stop responding and require a power-cycle to recover. The disk may also come with empty PBA already, but I think it's better to write it explicitly.)

dd if=/dev/zero of=/tmp/zeros.img bs=1M count=128
    128+0 records in
    128+0 records out
    134217728 bytes (134 MB, 128 MiB) copied, 0,0430394 s, 3,1 GB/s

sedutil-cli --loadPBAimage password1234 /tmp/zeros.img /dev/disk
    Writing PBA to /dev/disk
    ...
    19381540 of 134217728 14% blk=61334
    ...
    112363888 of 134217728 83% blk=61334
    ...
    134217728 of 134217728 100% blk=18936
    PBA image  /tmp/zeros.img written to /dev/disk
  1. Ensure MBR shadowing is enabled
sedutil-cli --setMBREnable on password1234 /dev/disk
    MBRDone set on
    MBREnable set on
  1. Enable global locking range
sedutil-cli --enableLockingRange 0 password1234 /dev/disk
    LockingRange0 enabled ReadLocking,WriteLocking
  1. Now your drive is configured. It will lock itself after a power cycle. Do it now.

  2. After the power cycle, your drive will be locked and empty shadow MBR will be presented. You may verify it:

sedutil-cli --query /dev/disk
    ...
    Locking function (0x0002)
        Locked = Y, LockingEnabled = Y, LockingSupported = Y, MBRDone = N, MBREnabled = Y, MediaEncrypt = Y
    ...

sedutil-cli --listLockingRanges password1234 /dev/disk
    Locking Range Configuration for /dev/disk
    LR0 Begin 0 for 0
                RLKEna = Y  WLKEna = Y  RLocked = Y  WLocked = Y
    ...

hexdump -C /dev/disk -n 512
    00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    *
    00000200
  1. a) Prepare hashed password file (if you preferred non-hashed password and added -n to sedutil-cli calls, skip this step and just write plaintext password to the password file)
cd sed-opal-unlocker
./sedutil-passhasher.py /dev/disk /somewhere/safe/mypassword.secret
    Checking /dev/disk...
    Found DISK MODEL with firmware FW_VER and serial b'1234567890           '
    Password hash will be written into /somewhere/safe/mypassword.secret
    Enter SED password for /dev/disk (CTRL+C to quit): <enter password1234>
    Hashed password saved! Protect that file properly (chown/chmod at least).

chmod 400 /somewhere/safe/mypassword.secret
chown root:root /somewhere/safe/mypassword.secret
  1. b) When deciding to encrypt the hashed password file, it will look like this:
cd sed-opal-unlocker
./sedutil-passhasher.py /dev/disk /somewhere/safe/mypassword.secret 1
    Checking /dev/disk...
    Found DISK MODEL with firmware FW_VER and serial b'1234567890           '
    Encrypted password hash will be written into /somewhere/safe/mypassword.secret
    Argon2id CPU cost = 13 iterations
    Argon2id MEM cost = 341.375 MB
    Argon2id threads  = 4
    Enter SED password for /dev/disk (CTRL+C to quit): <enter password1234>
    Enter passphrase for unlocking encrypted passwordhash file: <enter unlock passphrase>
    Enter passphrase again for verification: <enter unlock passphrase again>
    Use DMI data to generate passphrase salt?
    If you say Y, the passphrase will work only on this system. [y/n]: <enter "y" or "n">
    Hashed password saved! Protect that file properly (chown/chmod at least).

chmod 400 /somewhere/safe/mypassword.secret
chown root:root /somewhere/safe/mypassword.secret
  1. a) Now, finally, use the sed-opal-unlocker to unlock the drive:
cd sed-opal-unlocker
./sed-opal-unlocker unlock,MBRunshadow /dev/disk /somewhere/safe/mypassword.secret

If no errors were printed, it worked! Check yourself with commands from step 6. Chaining both operations together instead of separate two calls is especially useful when using encrypted password hash files - you wouldn't need to enter the passphrase twice.

  1. b) If you're interested as well (or only) in S3 sleep support:
cd sed-opal-unlocker
./sed-opal-unlocker s3save /dev/disk /somewhere/safe/mypassword.secret
  1. You may put 8a / 8b commands in some initialization scripts, initramfs, etc. Writing a .service file should be fairly trivial. Integrating encrypted password files with "plymouth ask-for-password" should be also pretty trivial. Good luck and have fun!

sed-opal-unlocker's People

Contributors

kylemanna avatar venemo avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.