Fix minor information leakage in gfss secret sharing#10284
Fix minor information leakage in gfss secret sharing#10284andrewjstone merged 2 commits intooxidecomputer:mainfrom
Conversation
The existing implementation leaks about 0.005 bits of information about the 8-bit secret.
e7edcb4 to
0cd2b64
Compare
|
Hi @tjade273. Thanks for the contribution. This is interesting. I had actually read the original report from cure53 for privy's implementation before writing this code, which is why I ended up making this change. I read the linked articles and have spent a bit of time thinking about this this morning. You are correct that from a purely information theoretic perspective, ensuring the high order coefficient is non-zero does leak information. However, we are not operating in purely information theoretic model. An attacker doesn't have to guess if K-1 shares can recompute the secret, they can try directly in our model. Specifically, the data being protected lives on U.2 drives in an Oxide rack. The key shares live on M.2 drives on the same rack. The M.2 drives require removal of the sleds from the rack for access, but are easily stolen. If we made the change suggested here then an attacker would only have to steal Importantly, the attacker doesn't have to guess that the leading coefficients are all zero. If for some reason they only had time to steal Without this change the attacker always has to steal at least I realize that differentiating the threat from stealing For these reasons, I'm heavily leaning against making this change. I am open to further discussion though, and really appreciate you taking a look. One big question I had for you is: Why you would ever want to split the same secret multiple times? |
|
Hey @andrewjstone - thanks for taking the time to think about this! For what it's worth, this is a bit of a nerd snipe and for the application to one-time sharing of U.2 keys the issue causes no security harm. If you don't want to read the following blob of text, nothing bad will happen so long as you do not at some point allow for re-sharing keys.
We see this occasionally in systems which allow for adding participants or changing thresholds. For example, you have a 3-of-5 secret and want add a new server to the rack without re-encrypting all the data. So you collect all the shares, recover the secret, then re-split the secret with a fresh polynomial. Or if a server dies and you want to reshare the secret (I know you don't do this, and instead recover the original share - other systems do it differently). If an adversary is able to get k-1 shares each time, it learns a little more data about the underlying value of the secret for each resharing. Now I'll address your objections to allowing zeros as leading coefficients.
There are two notions of "secret" that could be conflated here. There is the 1-byte secret which is the constant of a single SSS polynomial. Then there is the 32-byte secret that is used to derive the disk encryption keys. While an attacker can directly check if a 32-byte key is correct, they cannot easily determine if a single byte secret is correct - if they could, the system would be broken since the time to brute-force the encryption key would be On the other hand, the lack of zero coefficients leaks a data about the value of each individual byte. This is a much larger advantage than being able to check a whole 32-byte key at a time.
By the same reasoning, you should ban the value The attacker need not guess that the leading coefficients are all The point here is that any single choice of value for the leading coefficient is clearly insecure, but it's the distribution of values that matters. Similarly, why do you allow the possibility that the overall secret is Security is a distributional property, it doesn't make sense to claim that any particular value is insecure, only that a method for selecting a value is insecure. In practice, it often is a good idea to ban zero values - there are roughly three reasons for this:
None of these hold for the SSS case with GF_256 - in particular, number 2 does not hold because the probability of a zero leading coefficient appearing in correct operation is 1/256, which is not negligible. If the field was a 256-bit prime field, on the other hand, rejecting zero values might be a reasonable choice. Here's an informal analysis which gets at the distributional way of thinking about security. First you set up a game, then you decide a desired probability for how often the attacker wins. Let "Method A" be the technique which allows 0s, and "Method B" be the technique which rejects 0s. The overall security of this system is best phrased as a game: the honest dealer generates a random secret In the one-shot game, both methods are secure. Under Method A, it can be proven that the attacker's best strategy is to simply guess the secret or, equivalently, to guess the other share. Since for every true value of the secret Under Method B, there is a slightly better strategy for the attacker - for each byte first hypothesize that the leading coefficients on that SSS polynomial was zero, then guess any other byte. This wins with probability ~ However, in the repeated game, the attacker against Method B has a much stronger approach; for each resharing, note the value that was eliminated. After ~2000 re-sharings the attacker will have likely eliminated all values for each byte (https://en.wikipedia.org/wiki/Coupon_collector%27s_problem) and can guess the full secret. No such technique exists against method A - the attacker will have won with probability Thanks for reading :) |
|
Oh! @andrewjstone I just realized that I linked you the old Privy blog and not the one where they realized their mistake... https://privy.io/blog/zero-leading-coefficients-cryptography |
|
Thanks for the details @tjade273. You've convinced me. Especially since one of my other colleagues here suggested breaking this out into a separate crate that others can use arbitrarily. I think I was indeed conflating the per byte polynomial and the full secret of 32 bytes. I somewhat realized this as I was writing when I did the probability calculation that you would need all 32 bytes to be 0 ( I really appreciate you taking the time to lay this out. I feel like I should have gotten here sooner, but this is indeed tricky. The O(n log n) from the coupon problem also shows how you arrived at the ~2000 splits number. One other thing, CI is failing due to an extra newline. Can you |
The existing implementation leaks about 0.005 bits of information about the 8-bit secret.
In the Oxide use case, this is not a critical security issue as a single secret is not re-split multiple times. However future use cases might allow re-sharing of the same secret, which would reveal the secret to an attacker with only
k-1shares after about 2000 re-splits.See for example
https://www.zkdocs.com/docs/zkdocs/protocol-primitives/shamir/
https://privy.io/blog/shamir-secret-sharing-deep-dive