2
$\begingroup$

These are the steps:

  • Hash ECDH shared secret with SHA3-512

  • Concatenate odd bytes and rehash with SHA3-512 for AES-256 key

  • Concatenate even bytes and rehash with SHA3-512 for KMAC key

Is this key derivation secure? If not, what are the weaknesses?

$\endgroup$
6
  • $\begingroup$ Why don't you use Key Derivation Functions like PBKDF2 and Argon2? They are designed for this. You can easily derive more than one key from them. $\endgroup$
    – kelalaka
    Commented Oct 18, 2019 at 8:32
  • $\begingroup$ Because I'm hashing a high entropy key so I need to use a fast hash like sha2 or sha3, rather than Argon2 which is for low entropy keys $\endgroup$
    – SamG101
    Commented Oct 18, 2019 at 8:35
  • $\begingroup$ What are the parameters of the ECDH algorithm? The easiest way is to run a single SHA-512 or SHA3-512 split into two. If you want to have domain separation, you can use SHA-256( AES-KEY || secret) and SHA-256( KMAC-KEY || secret). $\endgroup$
    – kelalaka
    Commented Oct 18, 2019 at 10:30
  • $\begingroup$ The curve is SECP521R1. I like the idea of the domain separation you mentioned, thanks. $\endgroup$
    – SamG101
    Commented Oct 18, 2019 at 10:31
  • 1
    $\begingroup$ There are also Key Based Key Derivation Functions such as HKDF that can be used. NIST also specifies earlier (possibly faster) KBKDF's in a separate document (SP-108). HKDF is based on HMAC and doesn't use a work factor like the Password Based KDF's that @kelalaka mentions. $\endgroup$
    – Maarten Bodewes
    Commented Oct 18, 2019 at 11:15

2 Answers 2

3
$\begingroup$

The key derivation process you describe is reasonably secure in a technical sense, but not a good choice—it's not very flexible, it will raise auditors' eyebrows, and SHA3-512 is just not a good design.

Security if we model SHA3-512 as a uniform random function $H$: Each of the 512 bytes of $H$ on distinct DH secrets is independently uniform random, so the string of 256 odd-position bytes is uniform random and the string of 256 even-position bytes is independently uniform random. The probability of collision between the even-position bytes and the odd-position bytes is negligible, and in the event of no collision, the resulting keys are independent uniform random.

Why is it a bad choice?

  1. It's not very flexible. What happens when you want to derive another key? You've used up all the bytes (or, most of them, anyway); you'll need to find a different mechanism for this purpose, and it'll be another ad hoc thing that the remaining reasons still apply to. This fragility may hurt security: Will your successor who needs to extend the protocol make a decision that remains secure?

  2. It will raise auditors' eyebrows. This is not a standard construction for key derivation. By adopting this, you make an auditor's job harder by forcing them do cryptographic reasoning like the paragraph above, which auditors would rather not do. They don't want to think about the even-position and odd-position bytes of a SHA3-512 hash. They'd rather spend their effort simply confirming that you're using a well-studied and well-understood construction without having to think about it, so they can move on to higher-level protocol issues that will actually be more important in assessing the security of your application.

  3. SHA3-512 is just not a good design. SHA3-512 pays extra for an unimaginably high security level that doesn't matter to you for two reasons:

    • Collision attacks are not relevant to your application, but SHA3-512 pays a premium for collision resistance.
    • For political reasons rather than technical reasons, SHA3-512 was overdesigned to meet a meaningless security target.

    General advice aside: Ignore SHA3-256 and SHA3-512. Use SHAKE128 (with sufficiently long outputs) for a 128-bit security level. Use SHAKE256 only if you're a paranoid worrying about hedging against modest cryptanalytic advances, like for use with Ed448.


So what should you do instead? Here are a couple options:

  1. Use HKDF with your favorite hash function. SHA-256 is a perfectly respectable choice. If you insist on using SHA-3, you could use SHA3-256, but SHAKE128-256 would be faster for essentially same security—or you could just use SHAKE128 or KMAC128 directly, below.

    HKDF comes in two stages, which some APIs weld together:

    • HKDF-Extract takes initial key material and a salt and hashes them into a master key.

      • The initial key material is a high-entropy but possibly nonuniform secret, like the outcome of a DH key agreement.
      • The salt is optional; if it is merely distinct for each user, then it serves to mitigate multi-target attacks. For a DH key agreement that already achieves a 128-bit security level, like X25519, this is not necessary. But it doesn't hurt.
      • The master key is a short secret bit string that is effectively uniform random.

      \begin{equation*} k_{\mathrm{master}} = \operatorname{HKDF-Extract}(\mathit{dhsecret}) \end{equation*}

      You must never reuse the same initial key material. (Actually, in practical terms, if you reuse it with distinct salts, you won't get into security trouble—but you will make the auditor's job harder which will make your life harder and hurt your user's confidence in your system.)

    • HKDF-Expand takes a master key and an info parameter that uniquely describes a purpose and derives a subkey from them.

      • The master key is what you got out of HKDF-Extract. (Aside: It could also be from a password hash like Argon2 or PBKDF2, or any short uniform random secret key that you got from somewhere else and you just want to derive multiple subkeys from.)

      • The info parameter distinguishes the subkeys for different purposes: for each distinct info, there is an effectively independent uniform random subkey; it is as if you generated the subkey independently, but you can get it deterministically and reproducibly from the master key by passing in the same info.

      • The subkey is what you then use as an AES-256 key or whatever.

      \begin{align*} k_{\mathrm{AES}} &= \operatorname{HKDF-Expand}_{k_{\mathrm{master}}}(\text{‘AES encryption key’}), \\ k_{\mathrm{KMAC}} &= \operatorname{HKDF-Expand}_{k_{\mathrm{master}}}(\text{‘KMAC authentication key’}). \end{align*}

      You must never reuse the same info parameter for two different purposes. The auditor will want it to be easy to confirm that every purpose for which you derive a subkey has a distinct info parameter, so you should clearly document what each info parameter is used for, and if you ever programmatically format info parameters, make sure that you use a unique encoding of whatever data you put in them to ensure they will be unique.

  2. Use KMAC128, or SHAKE128, directly. For KMAC128, you can safely just feed in the DH secret as the KMAC128 key, and uniquely encode the salt and info parameters as the KMAC128 message. Similarly, you could also just uniquely encode the DH secret, the salt, and the info parameters as the SHAKE128 message: $\operatorname{SHAKE128}(\text{32-byte DH secret} \mathbin\| \text{32-byte salt} \mathbin\| \text{‘AES key’})$.

    This may be simpler for your application, and it doesn't hurt security. As long as it's conceptually structured the same way as HKDF—never reuse a DH secret except with distinct info parameters—it should be straightforward for an auditor to follow.

$\endgroup$
1
$\begingroup$

The easiest way maybe is running a single SHA-512 or SHA3-512 and split the result into half.

$$\text{AES-KEY}\mathbin\|\text{KMAC-KEY} = \operatorname{SHA-512}(secret)$$

Actually, it is better to use domain separation for arriving different keys from a single secret, or input key material. This can be achieved like:

$$\text{AES-KEY} = \operatorname{SHA-256}(\textit{"aes-key"}\mathbin\| secret)$$ $$\text{KMAC-KEY} = \operatorname{SHA-256}(\textit{"kmac-key"}\mathbin\| secret)$$

The prefixes can be considered as a nonce to separate the domain so that different hash results can be achieved for different results. This domain separation is also used in Key Derivation Functions.

Indeed, there are standardized ways of using hash functions to derive keys instead of work factor based on KDF's like PBKDF2.

For example, HKDF with HMAC from pycryptodome's The HMAC-based Extract-and-Expand key derivation function (HKDF). It is standardized in RFC 5869 and in NIST SP-800 56C.

    HKDFHKDF(master, key_len, salt, hashmod, num_keys=1, context=None)
  • master: The high entropy secret in your case ECDH key.
  • key_len: the desired output bit length in bytes for each key.
  • salt: a non-secret value strengthens the randomness extraction step. It can be used to mitigate multi-target attacks, rainbow-tables.
  • hashmod: The cryptographical hash function to use, SHA2, series.
  • num_keys: Number of the keys to derive.
  • context: An optional identifier can be used for domain separation.

Interestingly this library can output multiple keys at once. For example, you can call the below to arrive two AES-256 keys.

key1, key2 = HKDF(master_secret, 32, salt, SHA512, 2)

If you want, you can use the context and derive the two keys in different domain.

AES-Key = HKDF(master_secret, 32, salt, SHA512, 1, ""aes-key"")

KMAC-Key= HKDF(master_secret, 32, salt, SHA512, 1, "hmac-key")
$\endgroup$
4
  • $\begingroup$ Why not use a well defined KDF based on HMAC or KMAC instead of using this kind of derivation that directly uses a hash? Otherwise at least use KDF1, which is basically just a standardized way of using a hash. $\endgroup$
    – Maarten Bodewes
    Commented Oct 18, 2019 at 11:12
  • $\begingroup$ Also make sure you encode the secret to a constant size using e.g. I2OSP otherwise you may leak some information about the calculated shared secret (as a number). $\endgroup$
    – Maarten Bodewes
    Commented Oct 18, 2019 at 11:37
  • $\begingroup$ The Hazmat API is weird—calling hkdf.derive multiple times with the same salt and different keys is exactly the opposite of how HKDF should be used—and the documentation is bad. The purpose of the salt is not to ‘increase entropy’. The purpose of the salt is to mitigate multi-target attacks by using a different salt with each different user's initial key material. The info parameter is the parameter that you should vary for each different purpose: use the same initial key material and same salt multiple times, once with info="aes key" and once with info="kmac key", for example. $\endgroup$ Commented Oct 18, 2019 at 15:54
  • $\begingroup$ @SqueamishOssifrage Yes, the design is bad in that sense. I'll look into a better one. $\endgroup$
    – kelalaka
    Commented Oct 18, 2019 at 16:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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