cloudflarearchive / challenge-bypass-specification

WARNING: Deprecated! See Privacy Pass
https://privacypass.github.io
98 stars 7 forks source link

Token structure and verification #4

Open cowlicks opened 7 years ago

cowlicks commented 7 years ago

It has been pointed out that PSS should not be used for signing. But I also see:

the SIGN/VERIFY algorithms are RSA without modifications;

Which seems to indicate that "raw" RSA might be used. And every mention of the VERIFY function indicates it will use the same verification function on both the client and the edge, with just the public key. This would look like:

func verifiy(pubkey *RSAKey, token, signature *big.Int) bool {
    // check token is valid json...
    return token == signature.PowMod(pubkey.e, pubkey.n)
}

The tokens are json structured like {"nonce": ...}. Which means forging a signature would require finding some X such that:

Xe = X.PowMod(e, n)

Xe.StartsWith("{\"nonce\":") && Xe.Endswith("}")

So you have to get 10 characters right, which would take~2**30 steps. For disposable tokens this isn't so bad. However, for this verification function, the JSON structure is necessary because otherwise an attacker could forge signatures, by picking the signature and computing the token from it like:

sig = big.RandInt(0, n)
token = sig.PowMod(e, n)

lol

To fix this, the hash of the token nonce is what should be signed. So client signing request should send blinded hashes. And the verification should look like:

func verifiy(pubkey *RSAKey, token, signature *big.Int) bool {
    hash = Sha256(token)
    return hash == signature.PowMod(pubkey.e, pubkey.n)
}

This makes forging signatures as hard as solving Hash(tok) = sig.PowMod(e, n) for either tok or sig which is as hard as either solving a discrete log or inverting a hash.

This also removes any known plaintext from the blinded message which is nice.