• Breaking News

    Friday, December 14, 2018

    Cryptography Chacha20 based key derivation: a new hope

    Cryptography Chacha20 based key derivation: a new hope


    Chacha20 based key derivation: a new hope

    Posted: 13 Dec 2018 04:15 PM PST

    A few weeks ago, I tried and failed, to produce a trustworthy Chacha20 based key derivation function suitable for key exchange protocols. I think I finally found ways to do it.

    (Edit: I forgot to mention the end goal: minimising the number of primitives used to implement the key exchange and the subsequent AEAD. And I want to use Monocypher. Therefore, my only choice is to limit myself to X25519, Chacha20, and Poly1305. The current version of my handshakes use Blake2b to derive keys, and it works, I just want something more streamlined.)

    The problem

    The goal is to derive chaining keys from X25519 shared secrets. The gold standard for this is this:

    CK1 = HKDF(kex1) CK2 = HKDF(kex1 || kex2) CK3 = HKDF(kex1 || kex2 || kex3) CK4 = HKDF(kex1 || kex2 || kex3 || kex4) 

    (Edit: kex1, kex2, kex3, and kex4 are X25519 key exchanges, with either an ephemeral key or a static key at each end. There are 4 possibilities: ee, es, ss, se.)

    We are allowed to assume that, if the attacker doesn't know any of the relevant private keys, then HChacha20(kex, nonce) is as good as uniformly random, and can be use as a regular symmetric key. (The reason I believe this is a reasonable assumption, is because NaCl's crypto_box() itself uses HSalsa20 to mix shared secrets.)

    Finally, we want a satisfying security reduction about the key derivation.

    Security goals

    Shared secrets (kex1, kex2, kex3, kex4) are either:

    • unknown to the attacker (they don't know any of the two private keys);
    • known to the attacker (at least one private key is pwned);
    • partially controlled by the attacker (they have chosen their own key pair);
    • the same as another exchange (the attacker replayed a public key);
    • a known constant (the attacker has chosen a low order public key).

    If any shared secret involved in a key derivation is unknown to the attacker, then the derived key must be uniformly random, and independent from all the other chaining keys. The shared secrets themselves are not required to be independent.

    Broken attempt 1

    CK1 = HChacha20(kex1, 0) CK2 = HChacha20(kex2, 0) XOR CK1 CK3 = HChacha20(kex3, 0) XOR CK2 CK4 = HChacha20(kex4, 0) XOR CK3 

    If kex2 and kex3 happen to be the same, so will their hashes, and XOR will cancel them out. Thus, if the attacker knows kex1, they will know not only CK1, but CK3 as well (which in this case will be the same as CK1).

    So much for the naive approach.

    Broken attempt 2

    CK1 = HChacha20(kex1, 1) CK2 = HChacha20(kex2, 2) XOR CK1 CK3 = HChacha20(kex3, 3) XOR CK2 CK4 = HChacha20(kex4, 4) XOR CK3 

    We could conjecture that by using unique nonces instead of always zero, the four hashes will necessarily be independent. And they would be, if the key involved was uniformly random to begin with (that's at the heart of the Salsa20 security reduction).

    Except the kex are not uniformly random. They belong to a well defined subset of 256-bit strings. Assuming that the four hashes are uniformly random does not allow us to assume they are independent as well (remember that the kex are allowed to be identical). And we need that independence to make sure that the XOR won't cancel anything out.

    Broken attempt 3

    CK1 = HChacha20(HChacha20(kex1, 0), 1) CK2 = HChacha20(HChacha20(kex2, 0), 2) XOR CK1 CK3 = HChacha20(HChacha20(kex3, 0), 3) XOR CK2 CK4 = HChacha20(HChacha20(kex4, 0), 4) XOR CK3 

    This attempts to fix the second broken attempt. First, we hash the kex to derive a proper random key. Then we hash it again, with different nonces each time. We can assume, thanks to the XSalsa20 reduction, that HChacha20(key, x) and HChacha20(key, y) are independent, provided key is random, and x and y are different.

    There's a problem however: if the attacker happens to know a kex (say kex2), then two chaining keys (here, CK1 and CK2) will be related:

    CK1 = HChacha20(HChacha20(kex1, 0), 1) CK2 = known_value XOR CK1 

    The case could be made that Chacha20 is immune to related key attacks. After all, flipping a bit in the key space instead of flipping them in the nonce and counter space should have the same result: unpredictable, independent results. I never saw such an analysis anywhere though, so I'm not willing to trust anything important with it just yet.

    Solution 1

    CK1 = HChacha20(HChacha20(kex1, 0) XOR zero, 1) CK2 = HChacha20(HChacha20(kex2, 0) XOR CK1 , 1) CK3 = HChacha20(HChacha20(kex3, 0) XOR CK2 , 1) CK4 = HChacha20(HChacha20(kex4, 0) XOR CK3 , 1) 

    This one is another design altogether. We basically insert a HChacha20 between each chaining keys to try and make them independent from each other no matter what. Here's another way to write the above:

    CK1 = H1(H0(kex1)) CK2 = H1(H0(kex2) XOR H1(H0(kex1))) CK3 = H1(H0(kex3) XOR H1(H0(kex2) XOR H1(H0(kex1)))) CK4 = H1(H0(kex4) XOR H1(H0(kex3) XOR H1(H0(kex2) XOR H1(H0(kex1))))) 

    You can see that for each chaining key, each kex is hashed a different numbers of times:

    CK1 : 2 hashes for kex1, CK2 : 3 hashes for kex1, 2 hashes for kex2 CK3 : 4 hashes for kex1, 3 hashes for kex2, 2 hashes for kex3 CK3 : 5 hashes for kex1, 4 hashes for kex2, 3 hashes for kex3, 2 hashes for kex 4 

    That way, even if some kex is known, the affected chaining keys will still be unrelated, thanks to the different numbers of hashes. Let's assume for instance that kex2 is known to the attacker:

    CK1 = H1(H0(kex1)) CK2 = H1(known_value XOR CK1) 

    While known_value XOR CK1 is indeed related to CK1 itself, its HChacha20 is not: if kex1 is unknown, then CK1 is random, and so is known_value XOR CK1. The output of HChacha20 is random and independent from its input, if its input is a random key. Therefore, CK2 is unrelated to CK1. The same reasoning should apply for any number of known kex (except of course if all the kex of a given chaining key are pwned).

    Note that we could be tempted to use H0 for all hashes, but this may cause a problem in the case the attacker has knowledge of a kex, and active control of another kex. I'm not sure how critical it would be. So, let's tweek the above into this:

    CK1 = H(H(kex1)) CK2 = H(H(kex2) XOR H(H(kex1))) CK3 = H(H(kex3) XOR H(H(kex2) XOR H(H(kex1)))) CK4 = H(H(kex4) XOR H(H(kex3) XOR H(H(kex2) XOR H(H(kex1))))) 

    What if the attacker knows kex1, and replaces kex2 by H(kex1)? Here's what we would get:

    CK2 = H( H( kex2 ) XOR H(H(kex1)) ) CK2 = H( H(H(kex1)) XOR H(H(kex1)) ) CK2 = H(zero) 

    Therefore,

    CK1 = H(H(kex1)) CK2 = H(zero) CK3 = H(H(kex3) XOR H(zero)) CK4 = H(H(kex4) XOR H(H(kex3) XOR H(zero))) 

    The contribution from kex1 and kex2 are both gone from CK3 and CK4. Now this may not be very worrying in practice, because the attacker has to generate the kex they want with a public key they do not control. And almost every time, H(kex1) won't be on the curve (or its twist) to begin with. Still, the security assumptions of X25519 do not include the inability to generate the kex you want from a known public key. I thought more prudent to assume that attackers who control their end of the exchange control the resulting kex as well.

    By using different hashes, the attack above is thwarted: the attacker needs to find the value of kex1 such that H0(kex2) = H1(H0(kex1)), and I believe this would mean a key recovery from known plaintext (this may need further confirmation).

    Solution 2 (My favourite if it works)

    CK1 = HChacha20(kex1, 0) XOR HChacha20(zero, 1) CK2 = HChacha20(kex2, 0) XOR HChacha20(CK1 , 1) CK3 = HChacha20(kex3, 0) XOR HChacha20(CK2 , 1) CK3 = HChacha20(kex4, 0) XOR HChacha20(CK3 , 1) 

    It's a variant of Solution1, where we shift the hash separating the chaining key one slot to the left. I believe this makes the specification a bit more readable. Here's the alternate way of writing it:

    CK1 = H0(kex1) XOR H1(zero) CK2 = H0(kex2) XOR H1(H0(kex1) XOR H1(zero)) CK3 = H0(kex3) XOR H1(H0(kex2) XOR H1(H0(kex1) XOR H1(zero))) CK4 = H0(kex4) XOR H1(H0(kex3) XOR H1(H0(kex2) XOR H1(H0(kex1) XOR H1(zero)))) 

    The same reasoning about the number of hashes per kex applies. The same reservations about using the same nonce for both instances of HChacha20 apply.

    Questions

    • Are my assumptions reasonable?
    • Are my security goals sufficient?
    • Does my informal security reduction look legit?
    • Did I miss anything?
    submitted by /u/loup-vaillant
    [link] [comments]

    Formatting encrypted HDD.

    Posted: 13 Dec 2018 10:44 AM PST

    So I had asked question here about software to be used for full laptop/desktop HDD encryption.

    Veracrypt came up. Now I was wondering what would happen if I try to format either individual drive or boot up with new bootable USB and format the OS drive. Will I encounter any issues?

    Also is there any problems with encryption WRT life of HDD?

    submitted by /u/pradeep23
    [link] [comments]

    CBC Web Auth

    Posted: 13 Dec 2018 09:42 AM PST

    Is this scheme for storing secrets secure?

    Posted: 13 Dec 2018 01:04 PM PST

    Hi all,

    I'm elaborating a scheme for storing secrets such as credentials, passwords, etc., for my own use, and wanted to kindly ask if you guys see any obvious vulnerabilities. The general idea is to encrypt my secrets symmetrically with a strong password, and then store the cyphertexts by emailing them to my inbox automatically.

    A high-level overview is as follows: 1. I input a secret and a password into a utility tool. The tool encrypts the secret using the password, and emails the cyphertext to my account together with an identifier of my choosing which is easy to remember. 2. When I need to retrieve a secret, I search for its identifier in my email inbox and use the same utility tool to decrypt it by entering my password again.

    For encryption, I use AES 256 GCM. For each secret, a new 96-bit IV is generated. The generated IV is concatenated with the emailed cyphertext so that it can later be used for decoding.

    For deriving a 256 bit AES key, I run PBKDF2 on my password using a randomly-generated, 128-bit salt which is hardcoded into the code and is therefore public. I use 10k iterations for PBKDF2. This happens every time I want to create a new secret, or retrieve an existing one.

    All of this is coded in a Javascript utility tool (link) which uses no dependencies other than browser APIs, including the Web Crypto API. My belief is that as long as my OS, my browser, and the server serving the website are not compromised, my password should be safe. My secrets should also be safe even if someone managed to get ahold of my cyphered secrets, and potentially even a few plaintexts.

    Does this sound reasonable? Thank you all in advance.

    submitted by /u/ealmansi
    [link] [comments]

    No comments:

    Post a Comment