We're working on web3.0 decentralized internet, a big part of which is a decentralized file storage system where clients upload files to storage providers and pay them for the services. Storage providers return signed messages, with the content hashes included, as guarantees, such that if they lose the file and fail to respond to a verification challenge on the blockchain, by not being able to provide the proof they're still storing the file, they will lose their collateral.
Clients upload files to several storage providers at once for redundancy. We needed to make sure that providers can't collude with each other/one provider can't misrepresent itself as being several and take payments for N copies of the file, getting away with storing only one copy. At the same time, the original file contents should always be available to storage providers, since they're supposed to publicly serve it on the network by responding to requests for the content based on its hash. This means encrypting several copies with AES
The solution I initially came up with is to use pure RSA (not hybrid) algorithm in signature mode, i.e. when you encrypt with the private key, and decrypt with the public key (or, more precisely, sign a message with a private key, and recover the message from the signature using the complement of that, but the math is the same as encryption/decryption, just with swapped keys). That way:
- A client can generate one key pair per provider
- When storing file
F
with the providerN
, it can generate an "encrypted" replica ofF
(a signature containing the message) specific to that provider, by performingF_N = RSASign(F, k_priv_N)
, and sending the public keyk_pub_N
to the provider - Provider signs the guarantee on the
Hash(F_N)
, promising to store the encrypted copy - Provider can at any point extract the content by performing
F = RSARecover(F_N, k_pub_N)
which is analogous to feeding the public key to a decryption algorithm, calculate the real hashHash(F)
, and serve this file on the DHT network - However, all providers, even if they wanted to collude, must continue to store individual encrypted copies/signatures
F_N
because each signed off on their specificHash(F_N)
. There is no way to lose theF_N
, deliberately or accidentally, and then recreate it on demand just by havingF
andk_pub_N
, because that would require knowledge of the private key (which also explains why RSA must be in pure mode, and not in hybrid node, because performing AES encryption on provider side is trivial given the symmetric key, and they could get away with having to store just the RSA signature on top of the symmetric key, which is much smaller in size).
This works well, but we've ran into a few problems with this approach:
- RSA in pure mode is extremely slow
- Using proper padding for each block reduces the space we can use for embedding the file chunks, and therefore the resulting files are many percent bigger than the originals
Q: Are there any other similar cryptographic functions we can use for this case, which are superior to the RSA scheme in speed and size?
(For example, we've been looking at elliptic curves but there doesn't seem to be a way to extract the message, or even hash of the message, which would have also been helpful, from an EC signature.)
crypto.privateEncrypt
is not only poorly named. It's also poorly documented: it's default (and best) mode isRSA_PKCS1_PADDING
, but we have to guess what that is. And it turns out to be RSASA-PKCS1-v1_5 without the hash identifier or hash. If used directly for signature, one can find four messages such that the signature of one can be efficiently computed from the signature of the others, and there's a lot of flexibility in the choice of the messages. $\endgroup$