-
Notifications
You must be signed in to change notification settings - Fork 15
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
feat: add upper bound for encrypted random integers #44
Conversation
Add an upper bound for encrypted random integers by introducing a separate precompile that does a modulo operation before trivially encrypting the result. Generate encrypted random integers in the [0, upperBound) range where upperBound is a power of 2. If the given upperBound is not a power of 2, return an error. Reason for using powers of 2 is potential performance benefit when using an FHE-based random number generation in comparisson to an arbitrary upperBound. Avoid potential out-of-bound indexing of input in `FheLib`. The start index would be invalid if the actual input is exactly 4 bytes - in that case, indexing would start from index 4 and that doesn't exist. Fix by taking the min of 4 and len(input) - 1. Add unit tests for encrypted random number generation with an upperBound.
Heads up that taking the modulus is not a secure way to produce uniform numbers in the given interval. We're mocking everything here, so it's insecure anyways, but this approach is not something we can use for the secure version. One alternative is rejection sampling. To see the issue, if you for example sample
Using this to sample from [0;3) by taking the modulus results in:
since 0 = 3%3. More generally, the numbers in [0; next-power-of-2 - upper-bound) are twice as likely as the rest. |
Yes, I am aware of it, but thought since it is a mock, we can do a quick solution. I guess I can fix that and generate however many bits we want from the RNG and/or use bitshifts. Maybe makes sense to do it even if the whole thing is insecure, wdyt? |
I'm not sure what you mean by bitshifts, but yes, makes sense to do it now. It might match what we have to do in the secure version as well (eg sample a few times and use cmux to pick the one that's below). |
Let me explain my thinking of doing it with modulo.
If the upperBound is not a power of 2, then we can think of a different implementation that also might depend on how tfhe-rs produces randomness, not sure about it. Does that make sense? |
Yes, no problem here security wise.
Yes, I agree. If the modulus is a power of two then no security issue. But a bitshift would be faster of course.
This is what I suggest rejection sampling for, but there might be more efficient ways.
Yes 👍 |
Maybe I can change it such that if we are requesting say an 8 bit integer, I only get 1 byte from ChaCha20 and then, if the upperBound is set to a power of 2, I just do right shifts to get however many random bits we need from that 1 byte. And also for other types, 2 bytes for euint16 and so on. |
Use bitshifts instead of modulo for the upperBound in the rand family of functions. Also, get only the amount of bytes needed from ChaCha20, based on the data type.
I added a second commit with bitshifts. It is a bit more verbose than mod, but I guess the intent is clearer. Though these implementations will go away at some point. |
Btw, since we don't generate any random values and just simulate during gas estimation, if there is a decryption that depends on the random value (or is decrypting the random value itself), the decrypted value is always only 1 bits (i.e. max value possible for the type). That might mess up gas estimation as maybe user code expects a value in a certain range as requested by the upperBound. Since we will most likely remove inline decryptions, I think we can leave as is. What do you think? |
Yes let's just leave as is for now. |
Add an upper bound for encrypted random integers by introducing a separate precompile that does a modulo operation before trivially encrypting the result. Generate encrypted random integers in the [0, upperBound) range where upperBound is a power of 2. If the given upperBound is not a power of 2, return an error. Reason for using powers of 2 is potential performance benefit when using an FHE-based random number generation in comparisson to an arbitrary upperBound.
Avoid potential out-of-bound indexing of input in
FheLib
. The start index would be invalid if the actual input is exactly 4 bytes - in that case, indexing would start from index 4 and that doesn't exist. Fix by taking the min of 4 and len(input) - 1.Add unit tests for encrypted random number generation with an upperBound.