-
Notifications
You must be signed in to change notification settings - Fork 43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Discussion on power analysis attacks #195
Comments
H(priv || pub || pad to block (if pub isn't 32 bytes) || random || pad to block || message) is superior, as it increases the precomputable data, and still implements their counter measure I think (which works by having there be a compression with attacker unknown random data before any with attacker controlled inputs). |
It is, making the first padding unnecessary (we don't need to commit to the negation flag if we use the private key post-negation).
That would increase the number of compression function invocations. I'm not sure it adds anything? |
My reasoning was that there should be no attacker controlled data in a compression function run where the midstate is a constant secret. |
Rather than having a cached midstate and two compression functions, you could do |
I like that idea. And in general, implementing this protection seems to be a good idea. In the end it's never clear how effective these countermeasures are but this one here seem to make the attack at least harder and it does not introduce any drawbacks. |
Well the drawback is two compression function runs at runtime instead of one... but high speed implementations can implement alternative schemes. Also the drawback is that you can't use comparison to check what nonce implementation a black box is using if synthetic nonces are in use... but it seems in practice no one actually does this anyways (or even cares when that test returns a failure-- something claims outright to be 6979 but its results are not reproducible). |
Seems more likely than not that such attacks exist. At least it's easy to imagine an otherwise hash function that is vulnerable. In "Differential power analysis of HMAC based on SHA-2, and countermeasures" (google), McEvoy et al. stipulate an DPA attack that recovers the midstate of SHA256(x || m) where x is the secret key padded to the blocksize. Their countermeasure is using a hash function similar to SHA256 but where individual operations are masked.
|
This synthetic nonce idea added to the WIP reference code update PR: f84cd77 |
@jonasnick That's a great find. So I think the general principles are that you should never have (unmasked) secret data together with attacker-controlled data within one input block (because expansion is independent of midstate) or when updating a midstate in a compression step. That means the following things are insecure (assuming 16 byte
These however seem fine (each is listed with: total compressions; compression with precomputed key; compressions with precomputed key+rand):
So moving the randomness to the second block to enable precomputating My preference is thus |
Doesn't the second block consist of |
Oh, you're right. Adding that one:
|
|
Perhaps this isn't worth spending too much time on. Adding randomness to protect against power analysis can only protect the signature, but systems where attacks like this are a risk will likely also performing BIP32 derivation and Taproot tweaking, where randomness cannot help. Adding randomness remains useful as a countermeasure against fault attacks though, but the element ordering does not matter for that. |
We can do taproot tweaking without the private key though, so it should be easy to make safe? Though it means the "pubkey" you're signing for is somewhat attacker controlled, so you'd want If I'm reading this attack right, it allows you to recover I think you need more concrete implementation details to try to prevent side-channel attacks though, so I think I agree with it not being worth spending too much time on now. |
To be fair, any implementation where sha256 has side-channels is going to have an extremely hard time avoiding sidechannels in the rest of its logic. (not that I disagree with picking the more resistant structure as the recommended one) Edit: I see sipa said something similar, but I was actually thinking of the fixed base exp, the ex scalar mult and the addition with k rather than tweaking.
I had somehow (!) assumed in earlier discussions that rand would always be padded to 32 bytes (even if was empty), and had specifically brought up the compression function run having no attacker controlled data when suggesting h(rand||priv||pub||msg); I'm glad you made it clear that you were thinking of a different size! |
I agree with the preference but I can't follow why it's safe for any length of rand >= 16. |
You're right, leaks outside of nonce generation may not be as severe as I thought while writing the previous message here. Still, there are a number of them, though it's hard to say how serious they are:
Let's redo the math for 32-byte synthetic randomness (or randomness padded to 32 bytes):
With caveats:
I think my preference remains (a).
Simply because without, the secret data is insufficiently blinded before being mixed with attacker-controlled data. 16 may be absolute overkill though. If the attacker cannot observe the private key itself, but only a midstate that is a (deterministic) function of it, even a little bit of randomness added (say, 4 bytes) to the overall nonce means he's not going to be able to predict future nonces. |
How are you getting 1 compression with precomputed priv/rand? Are you forgetting the 8 byte length? This should be 3, 3, 2. I don't care that much, we should probably favour lowest credible leakage. Implementations which favour performance above other considerations will likely do something different. |
You're right; (a) and (b) are of course 3, 3, 2. |
Interesting thought. It looks like this countermeasure is not guarenteed to help. What may work is hashing untweaked
If the added randomness is going to become the default signing algorithm, it would be simpler to also use fixed length. It makes sense to view the pubkey as similarly dangerous as the message (whereas the attack on the privkey tweak appears to be significantly harder). Perhaps then use 32 byte a) with a rationale that hints at saving a compression if power analysis can be ruled out. |
What I had in mind was always fixed size, I assumed talking about different sizes is only about considering what would happen if people don't stick to the BIP. Shouldn't 23 bytes be enough then? Except that more randomness probably does not hurt, I have not seen an argument yet for why 32 bytes is significantly better than 23. |
23 only arose because in one of the layouts it was the maximum length that would fit without causing another compression function invocation. Anything over 16 bytes should be ample from a 'randomness' perspective, and anything over 32 starts becoming inconveniently much... the actual amount should be picked based on whatever s efficient with the hashing. It would be silly to take on an extra compression run because it used 32 instead of (say) 23. |
The argument in this thread is that with 23 bytes you'll have 9 bytes of pubkey in the same compression as |
Ah, I haven't seen this argument spelled out here explicitly. Is it really true that mixing attacker-controlled data even with secret data and randomness is an issue? The original attack mentioned here is against a scheme that doesn't use randomness at all. If it is indeed an issue, wouldn't then If the thing we need to avoid is secret data and attacker-controllable data in the same block, then |
The attacks discussed in this thread wouldn't apply because the midstate changes in each signing attempt due to rand32. |
Let's assume for a minute that secret key and randomness go into the first block and nothing else (by padding the rest). I wanted to know what helps and what doesn't. To do that, I wrote some code to analyze the behavior of the expansion step in SHA256, by propagating const/secret/random flags for all the integers in it. A complication is that the vulnerable step is an addition between 4 integers, and there's no good reason to assume any particular order in which they get added, so everything here is a worst case over all possible 24 orderings in which these additions occur (I am assuming sequential additions like (((a + c) + b) + d), and not hierarchical ones like (a + d) + (b + c), though). I'm treating any addition between (unblinded) secret data and other (unblinded) secret data as a problem, because in a taproot/tweak setting, the secret key is both a function of the secret and partially under control of the attacker. It may well be the case that this is not a concern at all, as much of the control the attacker has is limited due to the tweak being a hash, and there are linear modular operations getting in the way of thinking of bits individually. So, one observation is that mixing in randomness in there in general does not help. Of course, it's needed because otherwise the midstate becomes a target of attack for the next block, but the randomness does not affect the potential for leakage in the expansion of the first block itself - except in weird pathological configurations. Another finding is that any "sane" orderings are pretty bad - both key32+pad32 or pad32+key32 result in exposing information of up to 224 bits of key material (this is very likely a serious overestimation; it's just 7 chances for leakage in 32-bit operations). There are some crazy orderings that never expose more than 32 bits, like KPPKPPKPKPKKPKPK (where each letter represents 4 bytes). I don't think we're going to seriously propose something like that. So overall I think this must mean that we should treat operations between different secret key bits as safe. Not because we're sure that they are, but because if they aren't, there isn't anything non-crazy we can do about it. EDIT: seems I had a bug in my code, not all the above findings are correct. Will redo. |
heh, would be good to know if a crazy permutation actually looked safer, we could still use it in libsecp256k1 even if it wouldn't be what we would specify as a recommended procedure. |
What do people think of this: |
That's equivalent to doing Converting the "no entropy loss" argument, if you've got (Other than that, I like it) |
Isn't it still a problem that they're masked by the same randomness?
Interesting observation. Seems unlikely indeed assuming uniform distribution of the secret key even if you take attacker-controlled tweaks into account. |
Hm, this seems dangerous because collisions are possible in the input of the hash function. For example, if the least significant bit of the secret key is 0, then adding a tweak of 1 is equivalent to XORing What I say here assumes that the attacker can control the tweak, the public key and the randomness. I know that this a lot of assumptions but simply given the existence of this attack, I don't think this suggestion is the safest choice that we can make. Another reason why I don't like XORing with the secret key is that some genius could get the idea that Moreover, if the attacker sees/control certain bits of the randomness, XOR may not be a great choice when it comes to power analysis, because the attacker could try to find randomness values for which Maybe accepting an additional compression is the way to go. If you want high-speed signing, you probably don't want SHA256 anyway but are willing to change to BLAKE2 etc. |
I think you're missing that |
Oh yes, when I wrote that paragraph I missed entirely that Did you consider This has the disadvantage that it takes only 16 bytes of randomness but it has |
Since people seem to care about one compression, how about using either @sipa's or @real-or-random's (modified as above?) version, and mention the alternative Not sure, which version is better. @real-or-random's version can be easily extended to the rand32 version (by replacing rand16||rand16 and rand16 with rand32). OTOH, after a cursory look into the internals of SHA256 it seems at least plausible that 32 bytes randomness in @sipa's version improves randomization of the 64 fixed bytes over 16 bytes randomness.
@real-or-random do you have any specific analysis in mind? Or do you mean it's just easier to see that it works? |
@jonasnick I don't think anything is clearly better than anything else. Looking at https://eprint.iacr.org/2017/985.pdf, their measurements are mostly correlated with the Hamming weight of words being read from the chip's internal memory. That's of course only one piece of hardware, but perhaps it's a good model to target. We must of course assume some weakness in the attacker's powers - if he can read individual bits being loaded from memory for example, then no amount of randomization is going to matter. However, there is an important thing to notice here: it's not necessarily operations (bitwise or arithmetic) that can be targeted, but really any variable involved. Taking that into account, I think
I think I can't find something with similar blinding properties that doesn't have an additional compression. |
So I think my preference is either |
H(priv32 XOR rand32 || pub32 || msg32) feels particularly dangerous, you don't expect people will use the same random value for both the key and rand32? |
Sure (that's why I suggest the other options) - but I don't know how to weigh that risk against increased DPA risk and desire for fast compressions. |
What I had in mind was simply that anything that is a proper encoding of the inputs is nice because it's security (ignoring side channels) is a no-brainer.
I think we should choose the conservative option in the BIP. Singing performance is not our first priority, and implementations can choose other options if they want.
Can you explain why do you think it's bad to have randomness together with an attacker-controlled value? My understanding now is rather that this is fine, because the randomness is not fixed. Moreover, I'm not sure if it's a big difference if the attacker controls the value or just knows the value. For example:
Is I'm not only asking because of the
If it's okay to mix random/masked data with attacker-controlled data in a compression, then one could move I think the latter variant is particularly interesting because the private key comes in only the last compression, and the midstates are thus not secrets. I think the reason why we put private key in the beginning is to randomize the entire function early, similar to putting R in front for the challenge hash to avoid that the attacker controls the prefix. But while this is relevant for collisions, I don't think that's (too) relevant for the pseudorandomness of SHA256. There are also the variants |
Hm, I hadn't considered that. Since rand32 is supposed to be used only once, this likely would require the attacker to have higher resolution equipment than what seems to be used in these papers. As Tim also notes, using the constant pad32 instead of msg32 would probably not be any better.
The power analysis resistance is nice, but I'd be hesitant to suggest something that's so easily misused.
I don't think that mixing it with msg32 is different to mixing it with pub32 (which is similarly attacker controlled due to the tweak). Fwiw, with |
Consider Now instead consider
Does this make sense? It's classifying instances based on a selected bit in Now, instead consider |
It seems like in general @sipa's algorithm does not require combining Assume that the attacker is able to obtain Now if the attacker obtains a measurement |
You're right in so far that some bit-level information leaks inevitably, whenever On the other hand, with |
@real-or-random elsewhere suggested It also supports arbitrary length |
I like @real-or-random's idea. In addition to being misuse resistant it doesn't have the This also seems to be safe against the attacker controlling both Therefore, it's strictly better than the |
The ToDo left here is to add a rationale for our specific choice to the BIP. |
See also https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-March/017711.html for a summary of the above discussion. |
See https://eprint.iacr.org/2017/985.pdf, which describes an attack against Ed25519, by doing power analysis on the nonce generation function.
It relies on having a single SHA512 message block that contains both attacker-controlled data, and secret data that the attacker wishes to learn. We use SHA256 instead of SHA512, but it is sufficiently similar that we should perhaps at least consider if the same kind of attack could apply.
With #194, our nonce generation function would effectively become
H(priv || pub || msg [|| randomness])
. All the secret data goes into the first compression, and all attacker-controlled data goes into the second compression, so I think the attack does not apply directly already, but maybe related attacks do. I'll email the authors of the paper about this.The paper however suggests having at least 128 bits of randomness (synthetic nonce!) in the first hash block. I wonder if that means we should move the randomness to after the priv, so
H(priv || randomness || pub || msg)
. That means that if the randomness has at least 128 bits of entropy, we've effectively implemented their countermeasure, but at the cost of losing the ability to precompute the midstate atH(priv || pubkey || ...)
.Anyone have any thoughts?
The text was updated successfully, but these errors were encountered: