[Crypto] How can I force slow decryption in the browser?
Solution 1:
As I indicated in the comments, I believe a non-cryptographic solution may be the best for this task, where instead of attempting to measure "10 minutes" by the average amount of time it takes to do some computational task, you simply measure it by the wall-clock time. Note that this solution still uses some cryptography, but not as a proxy to measure 10 minutes.
Concretely, when a user wants the document, you can generate a key (say an AES private key) $k$, create a payload = $k || D$ where $D$ is the current date/time formatted in some standard way, let $h= H(k || D)$ be a hash of the payload, and then store $[h, k, D]$ in some table, and send $h$ to your user, along with the document (encrypted under the key $k$).
The user can then query you later with some hash $h'$. Upon recieving such a query, check if $h$ is in your stored table as part of some entry $[h, k, D]$. If so, and if at least 10 minutes have passed since the date/time $D$, return the key $k$ to the user, who can now decrypt. You can also evict the table entry $[h, k, D]$ at this point.
The pros/cons of this versus a "intentionally slow decryption" solution are:
There is less wasted computational effort on the part of the user
The limit of 10 minutes is enforced regardless of the user's particular architecture (and their desire to "cheat")
The operations on the server side of things are likely more efficient than the other RSA-based solution (for example, you have to use ~128 bits of randomness per user, instead of >2k for RSA to be secure).
The cons are:
- The user has to query your server a second time
- You must store some intermediate state for each user (the $[h, k, D]$). This should be rather small (a few hundred bits) per user, but is greater than zero.
I think it is worth mentioning that I expect it would be difficult to get a uniform "10 minute decryption time" in the browser using time lock puzzles, even among honest users. I have not thought through this in detail, but I imagine that differences in hardware architectures (especially with respect to the presence of certain SIMD instructions, i.e. Intel SSE / AVX type instructions) may be able to get a constant factor speedup on your users architectures in the underlying BigInt multiplication for RSA, which would then result in users having architecture-dependent decryption times.
If you implement things correctly (and make sure that auto-vectorization does not occur), you may avoid this issue. But even differences in clock speed (say 2GHz vs 3.5GHz, or whatever numbers are reasonable for a few-year-old phone vs a enthusiast's desktop) would likely make a big enough difference that it would be difficult to enforce a 10min decryption time for all users (you could likely ensure that decryption for all users takes at least 10 minutes, but for some users it may take 20 minutes, or whatever).
Solution 2:
I don't know about enforcing browser decryption, but here's an old trick for fast encryption and slow decryption if you understand RSA.
Generate a 2048-bit RSA modulus $N=pq$ and a random exponent $d$. Now solve $de\equiv 1\pmod{(p-1)(q-1)}$ ($e$ has to be secret so don't use 3, or 17, or 65537 or anything like that). Decrypting ciphertexts $c$ using $c^d\pmod N$ takes maybe 0.004 seconds using BearSSL (you might want to do your own benchmarking for some Javascript library), so we'll encrypt 150,000 times and it should take about 10 minutes to decrypt.
Here's the fun bit: we can giant step the encryption process. Compute $g\equiv e^{150000}\pmod{(p-1)(q-1)}$ which should take less than a hundredth of a second. Now take your message $m$ and compute $c_0\equiv m^g\pmod N$ which should less than a hundredth of a second. Now give the end-user $N$, $d$ and $c_0$ and get them to compute $c_{150000}$ where $c_{i+1}\equiv c_i^d\pmod N$. They should not be able to short cut this calculation without factoring $N$.
Promise me that you'll use this wisely; I'm still harrowed by the damage cryptography did to the world's power consumption with Bitcoin.
ETA this idea seems to be similar to the one linked by Mark in the comments.