mosajjal / dnspot

End-to-end Encrypted DNS Tunnelling and C2 framework
BSD 2-Clause "Simplified" License
59 stars 14 forks source link

AES GCM Nonce reuse #6

Open pix opened 1 year ago

pix commented 1 year ago

I was trying to understand the underlying crypto when I found this:

// Decrypt will provide a decryption mechanism for an arbitrary bytestream
// func (private *PrivateKey) Decrypt(data []byte) (decrypted []byte, err error) {
    hashed := sha256.Sum256(buf.Bytes())
    buf.Reset()

    block, err := aes.NewCipher(hashed[0:16])
    if err != nil {
        return
    }
    ch, err := cipher.NewGCMWithNonceSize(block, 16)
    if err != nil {
        return
    }
    decrypted, err = ch.Open(nil, hashed[16:], data[33:], nil)
    return
}

The hashed data will be the same given a pair of client/server, therefore the same key will be used with the same nonce more than once.

This StackOverflow post explains the situations: https://crypto.stackexchange.com/questions/26790/how-bad-it-is-using-the-same-iv-twice-with-aes-gcm

Even a single AES-GCM nonce reuse can be catastrophic.

  • A single nonce reuse leaks the xor of plaintexts, so if one plaintext is known the adversary can completely decrypt the other. This is the same as for a two-time pad.
  • In messages up to $\ell$ blocks long, after a single nonce reuse the adversary can narrow the authentication key down to $\ell$ possibilities with polynomial root-finding and thereby forge messages with high success probability $1/\ell$.
    • How? An $\ell$-block message is a polynomial of degree $\ell$ with zero constant term; the authenticator under secret key $r, s$ is $m \mapsto m(r) + s$, where $r$ is reused between messages and $s$ is derived as a secret function of the nonce. Find authenticators $a = m(r) + s$ and $a' = m'(r) + s$ for distinct messages under the same nonce, and only the ${\leq}\ell$ possible roots of the degree-$\ell$ polynomial $m(r) - m'(r) + a' - a$ in $r$ are possible values of the authentication key.

Use 96-bit nonces; if you don't—if, instead, you use smaller or larger nonces—it will be as if you chose nonces randomly, and the number of messages you can safely handle drops dramatically. But you can, of course, pad, say, a 64-bit nonce into 96 bits: what matters is only that the nonces be unique.

If the cost of a >32-bit nonce is prohibitive, you could periodically rekey. For example, if you first establish a long-term key $k$, you can use ${HMAC-SHA256}_k(i)$ as the key during the $i^{\mathit{th}}$ epoch, where each epoch covers four billion messages, and $i$ is encoded into a bit string in some canonical way.

Generating a nonce and appending it before the encrypted data and using this as the nonce should resolve the issue.

mosajjal commented 1 year ago

Hi.

thanks for this. I'll take a look and read upon this and will make appropriate changes.

mosajjal commented 12 months ago

@pix I'd be keen to dig deeper and come up with a proof-of-concept for this. let me add some logging here and there and see if we can get to plaintext with just sniffing the traffic.