2
$\begingroup$

I am studying an application with an AES crypto implementation that works that like the following:

  1. PBKDF2 is used with X iterations and SHA-256 to generate a 256-bit "master key" from a user-provided password and salt.

    master_key = pbkdf2(
        password,
        salt,
        iterations,
        alg: SHA256,
        size: 32)
    
  2. A crytpo-safe RNG is used to generate 512 bits of "key material" .

    key_material = random_bytes(
        size: 64)
    
  3. The key material is then encrypted with the 256-bit master key, unauthenticated.

    encrypted_key_material = encrypt_aes(
        data: key_material,
        key: master_key,
        hmac_key: null,
        mode: CBC)
    

    The encrypted key material is persisted on the device for later encryption use. The application user only needs to provide the PBKDF2 generated master key to decrypt it.

    It should be noted that the encrypt_aes function generates and uses a standard 16 byte random IV with every operation which is part of the returned encrypted bytes (along with the computed encrypt-then-mac HMAC data).

  4. When the application needs to encrypt some data, half of the key material is used as the encryption key and the other half is used as the HMAC key.

    key_material = decrypt_aes(
        data: encrypted_key_material,
        key: master_key,
        hmac_key: null,
        mode: CBC)
    
    encryption_key = key_material.split(0, 31)
    hmac_key = key_material.split(32, 64)
    
  5. And now encryption of other data can be performed in the application, which is authenticated.

    encrypted_data = encrypt_aes(
        data: "Hello World",
        key: encryption_key,
        hmac_key: hmac_key,
        mode: CBC)
    
    decrypted_data = decrypt_aes(
        data: encrypted_data,
        key: encryption_key,
        hmac_key: hmac_key,
        mode: CBC)
    
    // decrypted_data is now "Hello World"
    

I understand that CBC mode should always use HMAC authentication to protect against oracle attack vectors. As we can see here, an HMAC is used for encrypting data in the application (step 5), but it is not used when encrypting the random key material (step 3 and 4) that gives access to all of that data. This is presumably this way because PBKDF2-SHA256 does not produce enough bits to supply a 256-bit HMAC key for protecting the key material (step 3), hence why 512 bits of key material is derived separately for further encryption operations.

Is this a safe implementation? Does the fact that the key material is random byte data change its need to have HMAC authentication to protect it from attacks? Is the key material decryption from step 4 subject to the same attack vectors as standard string decryption, as in step 5?

$\endgroup$
  • $\begingroup$ Could you provide a link to the mentioned protocol? $\endgroup$ – Maarten Bodewes Jun 13 '18 at 13:04
  • $\begingroup$ @MaartenBodewes Sorry, the implementation related to this question is not publicly available. $\endgroup$ – izzle Jun 13 '18 at 13:22
3
$\begingroup$

Is this a safe implementation?

No. An adversary can change just the first encrypted block that contains part of the AES data key. Then HMAC authentication will succeed, but decryption with the AES key will result in the wrong plaintext message. If an IV is used then the IV can also be changed, resulting in only the first 128 bits of the AES data key to be changed.

After all, CBC has limited error propagation, and the block size of AES requires a minimum of 4 blocks to encrypt two 256 bit keys.

If padding is used then padding errors may also result in a padding oracle attack on the key material. But those would only work in a limited amount of scenarios.

Does the fact that the key material is random byte data change its need to have HMAC authentication to protect it from attacks?

No, not necessarily. It depends on how the random data is used. If alteration of the random data can help with creating an active attack then integrity / authenticity is required. To state that it doesn't need protection just because it is random data is certainly wrong.

Is the key material decryption from step 4 subject to the same attack vectors as standard string decryption, as in step 5?

No. The bits of the encrypted random data are unrelated to each other and they do not need to be padded, which thwarts many attacks on CBC mode. But it still has limited error propagation and no integrity / authenticity.


PBKDF2 should indeed be limited to generate only as many bits as the internal hash function provides. Generally you'd use an additional Key Based Key Derivation Function or KBKDF such as HKDF to derive the keys from a master secret, generated by PBKDF2:

$$K_{master} = \text{PBKDF2}(p, ...)$$ $$K_{enc} = \text{HKDF}(K_{master}, \text{"enc"})$$ $$K_{mac} = \text{HKDF}(K_{master}, \text{"mac"})$$

This doesn't require any additional storage as the labels "enc" and "mac" are simply constant values.


An ugly hack would be to also include the IV (if present) and ciphertext of the encrypted keys in the HMAC verification. I'm just mentioning this because the protocol may have used this already - it could be just a change of an offset - and it may have been overlooked.

$\endgroup$
  • $\begingroup$ Sorry, I forgot to include originally but yes, the encrypt_aes function generates and uses a standard 16 byte random IV with every operation which is part of the returned encrypted_data bytes (along with the computed encrypt-then-mac HMAC data). Updated question to include this info. $\endgroup$ – izzle Jun 13 '18 at 13:13
  • $\begingroup$ One follow up question regarding your suggestion on using HKDF. Is it safe to skip the extract step altogether and use the master_key directly as the PRK parameter with HKDF expand to generate two 256-bit keys for enc and mac? It seems the RFC states that this is ok to do (section 3.3), and I believe your example is also illustrating that. $\endgroup$ – izzle Jun 13 '18 at 15:15
  • 1
    $\begingroup$ Yes, that's certainly possible, you're starting off with a secret value where the entropy is already extracted, so no extraction is necessary, just HKDF-expand. I'd just request exactly the hash amount of bits from PBKDF2 to use as input material. $\endgroup$ – Maarten Bodewes Jun 13 '18 at 15:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.