As a beginner it seems that a scheme derived from a CSPRNG should be as secure as that CSPRNG. Is this a correct assumption? Are there restrictions or known special cases? A rough outline would be:

  1. Generate an IV using a non-deterministic CSPRNG.
  2. Use that IV to seed a deterministic CSPRNG.
  3. XOR each byte of input with a byte from the d-CSPRNG #2.
  4. Concatenate(IV, output of #3)
  5. Use key to seed a deterministic CSPRNG.
  6. XOR each byte of #4 with a byte from the d-CSPRNG #5

In pseudo-code:

# encrypt
iv = randomBytes(IV_SIZE)
c1 = xorUsingPRNG(plaintext, dCSPRNG(seed = iv))
iv_c1 = iv ++ c1
c2 = xorUsingPRNG(c1, dCSPRNG(seed = key))

# decrypt
iv_c1 = xorUsingPRNG(c2, dCSPRNG(seed = key))
iv = iv_c1[0:IV_SIZE]
c1 = iv_c1[IV_SIZE:]
plaintext = xorUSingPRNG(c1, dCSPRNG(seed = iv))

Both the Key and IV are of reasonable size (meaning they are significantly larger than just a few bits).

Are there any known attacks that would work on such a scheme - under assumption that the CSPRNG used is secure.

If the CSPRNG is secure and produces "random" numbers than the output of (any_data XOR random_numbers) should have the same properties in terms of randomness/distribution for as long as those two are independent.

If the CSPRNG is secure then even if one knows or choses the plaintext it shouldn't be feasible to reconstruct the seed used nor should it be possible to extract the keystream because of the randomization in #3.

Obviously the following doesn't work:

  1. Seed a d-CSPRNG with the key
  2. XOR input with output of d-CSPRNG

because this is trivially broken with known/chosen plaintext attacks.


Your scheme can be easily rewritten to adhere to a more common scheme:

  1. generate a random data key (called "the IV" in your scheme)
  2. use the data key to encrypt a message giving the ciphertext (using a stream cipher)
  3. wrap (encrypt) the data key using a cipher parameterized with the master key and store it with the ciphertext.

And the additional step:

  1. encrypt the ciphertext using a cipher parameterized with the master key as well.

Other than #4 this is a very common construction and known to be secure - given that the parameters are indeed configured / generated correctly.

It is also easy to see that the pass with the master key over the ciphertext is spurious if the previous encryption with the data key is secure. That means that a whole pass of a cipher over the ciphertext is performed for no reason, presuming that the stream cipher using the data key is secure.

Relying on deterministic CSPRNG's is often very dangerous from an implementation perspective. CSPRNG's are generally designed to produce random data. They may automatically reseed at times and the implementation may change between versions. A very good example of this is SHA1PRNG used in Java, which is not well described and had all of the described issues (and more) on Android.

Besides that, CSPRNG's are often designed to be very sturdy - a good stream cipher will generally beat the heck out of a CSPRNG in practice when it comes to generating output. The fact that a stream cipher could be seen as a CSPRNG (with a predetermined seed size and without reseeding) doesn't change that.

Likewise XOR with a static key stream is not a brilliant wrapping mechanism. If any data is leaked the data key (your IV) may be at risk. Theoretically it may be perfectly secure given that the message is randomized, but I'd rather use a wrapping mechanism found in FIPS certified hardware (for instance).

Note that the scheme does not protect the integrity of the ciphertext (nor the wrapped key, but the integrity of the wrapped key can be determined by decrypting integrity protected ciphertext). Generally you should try and use authenticated modes of encryption.

| improve this answer | |
  • $\begingroup$ We should stop mixing the terms CSPRNG's and stream cipher (without further qualification). Although a stream cipher can be seen as a CSPRNG with I/O limitations, the reverse is certainly not the case: you cannot just use a CSPRNG or DRBG as a stream cipher without requiring specific limitations on the CSPRNG in theory and the situation is even worse in practice. $\endgroup$ – Maarten Bodewes Sep 27 '18 at 13:39
  • $\begingroup$ Looking at it again I've noticed that if you skip the IV (in the ciphertext) what you're left with is (plaintext XOR rnd1 XOR rnd2) which is essentially (plaintext XOR keystream_i) which means you can extract the key stream and start encrypting your own texts with it without knowing the key and just re-use the encrypted IV. This is not be enough to actually decrypt anything but at least it allows you to encrypt arbitrary plaintexts to valid ciphertexts without knowing the key which pretty much breaks the security of such a scheme. $\endgroup$ – mroman Sep 27 '18 at 14:16
  • $\begingroup$ Unless one counts that as an integrity issue then I guess it doesn't break the scheme itself because the scheme requires external integrity but my intuition tells me that for a secure scheme it at least shouldn't be easy to be able to encrypt arbitrary plaintexts to valid ciphertexts (that decrypt back to the plaintext) without knowing the key. $\endgroup$ – mroman Sep 27 '18 at 14:18
  • $\begingroup$ Generally a cipher should be protected against an adversary that is allowed to encrypt arbitrary plaintext. Allowing an adversary to perform encryption is - strange as it may sound - not the same as breaking the cipher. Besides that, the whole notion of valid ciphertext only comes into play if there is something that verifies the ciphertext or plaintext to be valid. The best way to do that is to use authenticated encryption and verify the authentication tag before decrypting (or using) the plaintext message. Or, if data comm is intended, an entire secure transport protocol. $\endgroup$ – Maarten Bodewes Sep 27 '18 at 14:25
  • $\begingroup$ No, you're still OK I think: the confidentiality of the plaintext is all an encryption scheme / cipher cares about. It does therefore count as an integrity issue. Note that the adversary should still not be able to infer an IV nor enough information to decrypt unknown plaintext. Beware not to try and attack a cipher by inferring a real usage scenario: use the informational theoretic notions of security instead. Protecting against real usage scenarios is performed by a protocol. $\endgroup$ – Maarten Bodewes Sep 27 '18 at 14:57

As a beginner it seems that a scheme derived from a CSPRNG should be as secure as that CSPRNG.

As for specific cases and exceptions, there is a general case in that a secure CSPRNG doesn't necessarily make for an encryption system of any sort. It's all in the way you construct the system around the CSPRNG. For example if you perform #6 onto #4 and encrypt your IV, how will anyone decrypt it the message?

So if you go down the route of an IV based system, you have to handle the IV issues. Repeating the IV under a fixed key is a no no. How do you prevent that, especially when you reboot? An 8 bit IV isn't great either from a brute force perspective. These trivial examples demonstrate that unfortunately your assumption is incorrect.

Being a little more specific, look at AES timing attacks which refers to Cache-timing attacks on AES. As a programmer, you'll understand it better that I. If your CSPRNG happens to use some form of AES counter construct, simply producing perfectly random numbers is not always adequate. This is especially true if the bad guys have total access to the device running your CSPRNG, such as a smart card. Your keys might even reside in a cache for a long time, and people have been known to do key recovery with silly exotic gasses.

Non deterministic random number generators can also be tricky. A TRNG can go wonky and sometimes it's difficult to tell. There is the issue of malleability /authenticity too. I was not intending to list all insecurities or analyse your scheme.. All these examples simply demonstrate that just producing good random numbers is not adequate as you initially assumed.

| improve this answer | |
  • $\begingroup$ In other terms: you create an IV.. encrypt the message with the CSPRNG seeded by the IV. Then add the IV and encrypted message together and encrypt this again using the CSPRNG seeded by the key. Decryption is thus just the reverse of that. You decrypt it with the CSPRNG seeded by the key and then you also get the IV and then you decrypt the rest with the CSPRNG seeded by the IV. The IV is generated randomly (using a non-deterministic CSPRNG) which makes using the same IV extremely unlikely. Obviously both the IV and Key have to be of a reasonable size. I'll mention that in my post. $\endgroup$ – mroman Sep 27 '18 at 12:57
  • 1
    $\begingroup$ It's good that you're addressing the IV. My little examples were just to prove that a secure CSPRNG alone isn't anywhere near enough for a secure cipher. $\endgroup$ – Paul Uszak Sep 27 '18 at 13:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.