SGX is a way of running security-sensitive user-mode code in an ‘enclave’. Code running in an enclave has its memory encrypted and authenticated, and cannot be observed by code running anywhere else. It’s able to use device-specific keys to encrypt (‘seal’) data to future executions of itself or enclaves signed by the same key.
We can use SGX to harden password hashing, by imposing the restriction that it is only possible on our hardware. That means offline attack is no longer possible, and a database leak only contains undecryptable ciphertext.
This is a demo, and the result of me playing with SGX for a day. You shouldn’t deploy this without understanding everything yourself first.
It would be easy to hash a password in an enclave and then seal it to itself for verification later. Unfortunately, that means only that physical CPU will ever be able to check a user password. That’s not a sensible approach; backups would be worthless.
So we’ll have a logical grouping of enclaves, all of which can check passwords – a ‘region’. A region is implemented as an AES key available to all such enclaves. ‘Enrolling’ an enclave into a region involves the enclave learning the region key, and sealing the key material to itself for later use.
When we’re not enrolling an enclave into our region, the region key can be kept offline, in a hardware security module, or written down in a safe.
We need only two operations: setting a password and checking a guess. These look like:
Here, we generate a random salt (using laundered hardware entropy from the
instruction) and pass the password into PBKDF2. The result and salt are encrypted using
the region key, producing a ciphertext which can be stored alongside the user record
in a database or password file.
Here we decrypt the ciphertext to obtain the salt and correct hash. We hash the purported password and compare with the correct hash.
The code is here. The main enclave code is in pwenclave/pwenclave.c. The interface to it is in pwenclave/pwenclave.edl, in Intel’s “Enclave Description Language”, which tells the tooling which function calls you want remoted into (an ‘ecall’) and out of (an ‘ocall’) the enclave.
There’s a test program in smoketest/smoketest.c which should product output like this:
pw_region_enroll took 0ms pw_setup took 78ms setup worked, blob is 84 bytes 43fb1bbfda8e71b2fed8c714f7d5b16730c184 25595b34c4fc93280471967a7377ce251ff41f a43068246eff231a3a86af6c50ef64e4b26d18 31d9e885c1ad577672c8c1f7beae8ee854e829 3f18d5b74e29f350 pw_check+ took 78ms pw_check worked (positive case) pw_check- took 78ms pw_check worked (negative case)