Subscribe

Lenovo ThinkPad HDD Password

Modern SSDs (at least the ones made by Intel, Samsung) always encrypt all
stored data using AES. The encryption key used is stored in nonvolatile memory
on the SSD. One of the reasons for this is that to securely wipe the drive now
you just need to overwrite the encryption key with a new random one. This way,
you don’t need to erase every flash block, which is very bad for durability
reasons. The encryption key can optionally be encrypted using a 32-byte
“security password”, the configuration of which is overloaded on the ATA
security feature set. If you trust the hardware manufacturer to actually
implement this securely, this would seem to provide a very solid and fast
option for encrypted persistent storage.

To be able to boot off of such an encrypted drive, it needs to be unlocked
before the OS’s bootloader can be read, which requires BIOS support. Luckily,
my Lenovo ThinkPad T420s does support this: you can configure a drive password
in BIOS the setup screen and from then on the BIOS will ask for a password upon
startup. Now here’s the catch: it turns out that when you take this drive and
put it in a different machine, it is impossible to unlock the drive. This would
mean that if my laptop dies but the drive were still intact I would be unable
to access the data on the drive, even though I know the password!

A couple of weeks ago I finally decided to get to the bottom of this by reverse
engineering the Lenovo UEFI BIOS on my laptop. The goal was simple: to find the
code path from password input to ATA security unlock output and reproduce
it. I have detailed the reverse engineering process in another blog
post. Here’s the
algorithm:

The inputs are \textit{Password} which is the user-supplied password and
\textit{AtaIdentity} which is the ATA Identify Device data
structure.
The output \textit{AtaPassword} gets sent to the drive. Why do they use
this algorithm? It’s actually somewhat clever: the S/N and M/N act as a salt,
such that a hash sniffed off of the ATA bus will only be able to unlock that
one drive, and not any other drives that use the same password.

That’s the good part. The bad part is that the algorithm above is not quite
complete. Here is the actual algorithm:

The function \textrm{ToScanCodes} translates the characters
1234567890qwertyuiopasdfghjkl;zxcvbnm␣ to integers in the ranges 2–11, 16–25,
30–39, 44–50, 57–57, respectively, while dropping other characters.
\textrm{SHA}_{256} is the well-known hash function. \textrm{SwapBytes}
is the POSIX swab function, it swaps odd and even bytes.

There are a couple of peculiarities in the algorithm that reduce the security.
First of all, I’m not sure why the characters get converted into scancodes. The
UEFI BIOS is well-equiped to deal with keyboard layouts, so that just seems
unnecessary. It also reduces the entropy to only 5.3 bits per character, making
short passwords very insecure. What’s worse though, is that only 12 bytes of
the password hash are used, putting an upper bound of 96 bits on the entropy.
If your password is sampled uniformly at random from the available scancodes,
don’t bother making it longer than 18 characters.

The other weird thing is the \textrm{SwapBytes} function. This means that
if your model number is
Samsung␣SSD␣840␣EVO␣500GB␣…, that part of the
input to the hash function will be
aSsmnu␣gSS␣D48␣0VE␣O05G0␣B…. Why is that?
Between the ATA Identify Device data structure being defined in terms of 16-bit
words and the UEFI specification using 16-bit wide characters, while the model
and serial number are encoded as 8-bit ASCII, I can only assume that someone
messed up some endianness conversion somewhere.

Today, am I releasing a tool to unlock your
drive. If despite all the
above—96 bits is more entropy than most passwords have—you still decide to use
the Lenovo BIOS to do your password management, you can use this to unlock your
drive in the event of hardware failure. You will need hdparm to talk to your
drive. If the password hash contains a ␀ character, you’ll need to patch
hdparm to be able to use that. I tested this on my own setup, but you may
want to verify it actually works before you start depending on it.